Skip to content

Instantly share code, notes, and snippets.

@J0onYEong
Created March 31, 2024 15:17
Show Gist options
  • Save J0onYEong/07012d406518f2eaee7819b5a509d9de to your computer and use it in GitHub Desktop.
Save J0onYEong/07012d406518f2eaee7819b5a509d9de to your computer and use it in GitHub Desktop.
[RxSwift+MVVM] 특정 뷰에속할 경우 상태가 변경되는 UITableViewCell입니다.
import Foundation
class NumberElementModel {
let id: Int
let number: Int
var isInside: Bool
init(id: Int, number: Int, isInside: Bool) {
self.id = id
self.number = number
self.isInside = isInside
}
}
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let viewModel = TableListViewModel()
let disposeBag = DisposeBag()
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.rowHeight = 56
tableView.register(TableViewCell.self, forCellReuseIdentifier: String(String(describing: TableViewCell.self)))
return tableView
}()
let detectArea: UIView = {
let view = UIView()
view.backgroundColor = .black.withAlphaComponent(0.1)
view.isUserInteractionEnabled = false
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
setAutoLayout()
setObservable()
}
func setAutoLayout() {
view.addSubview(tableView)
view.insertSubview(detectArea, aboveSubview: tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
detectArea.heightAnchor.constraint(equalToConstant: 100),
detectArea.centerYAnchor.constraint(equalTo: view.centerYAnchor),
detectArea.trailingAnchor.constraint(equalTo: view.trailingAnchor),
detectArea.leadingAnchor.constraint(equalTo: view.leadingAnchor),
])
}
func setObservable() {
viewModel
.numberListObservable
.observe(on: MainScheduler.instance)
.bind(to: tableView.rx.items(cellIdentifier: String(describing: TableViewCell.self), cellType: TableViewCell.self)) { _, item, cell in
cell.numberLabelView.text = "\(item.number)번"
cell.stateLabelView.text = item.isInside ? "겹침" : "안겹침"
cell.backgroundColor = (item.isInside ? UIColor.blue : UIColor.red).withAlphaComponent(0.3)
cell.onStateChange = { [weak self] isInside in
self?.viewModel.changeState(isInside: isInside, id: item.id)
}
}
.disposed(by: disposeBag)
}
}
extension ViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let tableView = scrollView as? UITableView else {
return
}
tableView.visibleCells.forEach { cell in
guard let tableViewCell = cell as? TableViewCell else { return }
// tableView를 기준으로한 Cell의 프레임을 detectAreat뷰의 좌표계로부터의 프레임
let frameFromDetactArea = tableView.convert(tableViewCell.frame, to: detectArea)
let isInside = (frameFromDetactArea.origin.y+frameFromDetactArea.height) >= 0 && frameFromDetactArea.origin.y <= detectArea.bounds.height
tableViewCell.onStateChange?(isInside)
}
}
}
import Foundation
import RxSwift
import RxCocoa
class TableListViewModel {
let numberListObservable: BehaviorRelay<[NumberElementModel]> = BehaviorRelay(value: [])
init() {
numberListObservable
.accept(Array(1...100).map({
NumberElementModel(id: $0, number: $0, isInside: false)
}))
}
func changeState(isInside: Bool, id: Int) {
_ = numberListObservable
.take(1)
.map { elements in
if let model = elements.first(where: { $0.id == id }) {
model.isInside = isInside
}
return elements
}
.subscribe(onNext: {
self.numberListObservable.accept($0)
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment