Cla*_*aus 1 mvvm ios reactive-cocoa swift reactive-cocoa-4
我是一个初学者,第一次使用ReactiveCocoa和 Swift。我正在构建一个显示电影列表的应用程序,并且我正在使用 MVVM 模式。我的视图模型如下所示:
class HomeViewModel {
let title:MutableProperty<String> = MutableProperty("")
let description:MutableProperty<String> = MutableProperty("")
var image:MutableProperty<UIImage?> = MutableProperty(nil)
private var movie:Movie
init (withMovie movie:Movie) {
self.movie = movie
title.value = movie.headline
description.value = movie.description
Alamofire.request(.GET, movie.pictureURL)
.responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
self.image.value = image
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想在 UITableView 中配置我的单元格,如下所示:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MovieCell", forIndexPath: indexPath) as! MovieCell
let movie:Movie = movieList[indexPath.row]
let vm = HomeViewModel(withMovie: movie)
// fill cell with data
vm.title.producer.startWithNext { (newValue) in
cell.titleLabel.text = newValue
}
vm.description.producer.startWithNext { (newValue) in
cell.descriptioLabel.text = newValue
}
vm.image.producer.startWithNext { (newValue) in
if let newValue = newValue {
cell.imageView?.image = newValue as UIImage
}
}
return cell
}
Run Code Online (Sandbox Code Playgroud)
这是反应性可可的正确方法吗?我是否需要将标题和描述声明为可变或只是图像(是唯一改变的)。我想我可以使用绑定,但我不知道如何继续。
为了使用 Reactive Cocoa + MVVM 模式来做到这一点,我首先将所有逻辑从其视图模型移动到单元类本身来配置单元。然后从 viewModel 中删除 MutableProperties (它们实际上并不可变,我们不需要这些信号)。对于图像,公开一个信号生成器,该信号生成器将在start()调用时执行网络请求以获取图像,而不是init在 ViewModel 上调用时隐式获取图像,给我们类似的东西
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MovieCell", forIndexPath: indexPath) as! MovieCell
cell.viewModel = self.viewModelForIndexPath(indexPath)
return cell
}
private func viewModelForIndexPath(indexPath: NSIndexPath) -> MovieCellViewModel {
let movie: Movie = movieList[indexPath.row]
return HomeViewModel(movie: movie)
}
Run Code Online (Sandbox Code Playgroud)
进而
class MovieCell: UITableViewCell
@IBOutlet weak var titleLabel: UILabel
@IBOutlet weak var descriptionLabel: UILabel
@IBOutlet weak var imageView: UIImageView
var viewModel: MovieCellViewModel {
didSet {
self.configureFromViewModel()
}
}
private func configureFromViewModel() {
self.titleLabel.text = viewModel.title
self.descriptionLabel.text = viewModel.description
viewModel.fetchImageSignal()
.takeUntil(self.prepareForReuseSignal()) //stop fetching if cell gets reused
.startWithNext { [weak self] image in
self?.imageView.image = image
}
}
//this could also go in a UITableViewCell extension if you want to use it other places
private func prepareForReuseSignal() -> Signal<(), NoError> {
return Signal { observer in
self.rac_prepareForReuseSignal // reactivecocoa builtin function
.toSignalProducer() // obj-c RACSignal -> swift SignalProducer
.map { _ in () } // AnyObject? -> Void
.flatMapError { _ in .empty } // NSError -> NoError
.start(observer)
}
}
}
Run Code Online (Sandbox Code Playgroud)
并在视图模型中
struct HomeViewModel {
private var movie: Movie
var title: String {
return movie.headline
}
var description: String {
return movie.description
}
func fetchImageSignal() -> SignalProducer<UIImage, NSError> {
return SignalProducer { observer, disposable in
Alamofire.request(.GET, movie.pictureURL)
.responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
observer.sendNext(image) //send the fetched image on the signal
observer.sendCompleted()
} else {
observer.sendFailed( NSError(domain: "", code: 0, userInfo: .None)) //send your error
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1139 次 |
| 最近记录: |