Rx를 이용해서 가장 기본적인 UITableView를 만드는 법에 대해서 정리해보겠습니다.
|Observable| ---(bind)--- |UITableView.rx.items|
이렇게 이어주기만 하면 끝입니다.
앞으로 UICollectionView나 다른 UI와 엮어보기도 할 텐데, 원리는 동일합니다.
import UIKit
import SnapKit
class WishListCell: UITableViewCell {
static let reuseIdentifier = "WishListCell"
let wishItemLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupAttribute()
setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setupData(_ data: String) {
wishItemLabel.text = data
}
private func setupAttribute() {
wishItemLabel.font = .systemFont(ofSize: 15)
wishItemLabel.textColor = .darkGray
wishItemLabel.textAlignment = .center
}
private func setupLayout() {
contentView.addSubview(wishItemLabel)
wishItemLabel.snp.makeConstraints {
$0.top.bottom.left.right.equalToSuperview()
}
}
}
import RxSwift
import Foundation
class MainViewModel {
var wishItem: [String]
init() {
// dummy data
wishItem = ["MacBook M1 Pro", "iPhone 13 Pro max", "iPad Pro 15"]
}
func getCellData() -> Observable<[String]> {
return Observable.of(wishItem)
}
}
getCellData
에 파라미터로 값을 전달받아도되고, 서버를 통해서 전달받는 경우는 여기서 다시 다른 옵저버블에서 호출해야겠죠?import RxCocoa
import UIKit
import RxSwift
import SnapKit
class MainViewController: UIViewController {
// MARK: - Properties
let disposeBag = DisposeBag()
private let tableView = UITableView()
// MARK: - Initialize
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - View Lifecycle
extension MainViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupAttribute()
setupLayout()
}
}
// MARK: - Binding
extension MainViewController {
public func bind(_ viewModel: MainViewModel) {
viewModel.getCellData().bind(to: tableView.rx.items) {
(tableView: UITableView,
index: Int,
element: String)
-> UITableViewCell in
guard let cell = tableView.dequeueReusableCell(withIdentifier: WishListCell.reuseIdentifier) as? WishListCell else { fatalError() }
cell.setupData(element)
return cell
}
.disposed(by: disposeBag)
}
}
// MARK: - Helpers
extension MainViewController {
func setupAttribute() {
tableView.register(WishListCell.self, forCellReuseIdentifier: WishListCell.reuseIdentifier)
tableView.rowHeight = 100
}
func setupLayout() {
view.addSubview(tableView)
tableView.snp.makeConstraints {
$0.top.bottom.left.right.equalToSuperview()
}
}
}
// MARK: - Binding
extension MainViewController {
public func bind(_ viewModel: MainViewModel) {
viewModel.getCellData().bind(to: tableView.rx.items) {
(tableView: UITableView,
index: Int,
element: String)
-> UITableViewCell in
guard let cell = tableView.dequeueReusableCell(withIdentifier: WishListCell.reuseIdentifier) as? WishListCell else { fatalError() }
cell.setupData(element)
return cell
}
.disposed(by: disposeBag)
}
}
cellForRowAt
처럼 구성하면 됩니다.참고로 SceneDelegate에서 viewModel을 전달하고 바인딩해주고 있습니다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
let mainViewModel = MainViewModel()
let rootViewController = MainViewController()
rootViewController.bind(mainViewModel)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
}
이번 글은 아주 기본적인 UITableView를 Rx로 구성했습니다. 이후에 글에서는 이곳에 하나씩 살을 붙인 글을 작성 예정입니다.
읽어주셔서 감사합니다.