我是初学者,第一次使用 ReactiveCocoa 和Swift。我正在构建一个显示电影列表的应用程序,并且我正在使用MVVM模式。我的ViewModel看起来像这样:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int func(int a)
{
//assume the data is big and writing takes a long time
int data = a;
return data;
}
int main()
{
ifstream in("numbers.txt");
int a;
while(in >> a)
{
stringstream ss;
ss << a%10;
string str;
ss >> str;
str += "_File.txt";
ofstream out(str.c_str(), fstream::in | fstream::out | fstream::trunc);
//This is blocking, if write takes long
//but can be rewritten in a multi-thread fashion
// to allow upto 10 simultaneous file write
out << func(a) << endl;
}
return 0;
}
我想在UITableView中配置我的单元格,如下所示:
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
}
}
}
}
这是Reactive Cocoa的正确方法吗?我是否需要将标题和描述声明为可变或仅仅是图像(是唯一的变化)。我想我可以使用绑定,但我不确定如何继续。
答案 0 :(得分:2)
使用Reactive Cocoa + MVVM模式执行此操作我将首先移动所有逻辑以将单元格从其viewmodel配置到单元类本身。然后从viewModel中删除MutableProperties(它们实际上并不可变,我们不需要那些信号)。并且为图像公开一个信号生成器,它将在调用start()
时执行网络请求以获取图像,而不是在ViewModel上调用init
时隐式获取它,给我们类似的东西
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)
}
然后
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)
}
}
}
并在ViewModel中
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
}
}
}
}