Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 使用ReactiveCocoa(4.2.1)和Swift异步加载图像_Ios_Swift_Mvvm_Reactive Cocoa_Reactive Cocoa 4 - Fatal编程技术网

Ios 使用ReactiveCocoa(4.2.1)和Swift异步加载图像

Ios 使用ReactiveCocoa(4.2.1)和Swift异步加载图像,ios,swift,mvvm,reactive-cocoa,reactive-cocoa-4,Ios,Swift,Mvvm,Reactive Cocoa,Reactive Cocoa 4,我是第一次在Swift中使用ReactiveCocoa的初学者。我正在构建一个显示电影列表的应用程序,我正在使用MVVM模式。我的ViewModel如下所示: class HomeViewModel { let title:MutableProperty<String> = MutableProperty("") let description:MutableProperty<String> = MutableProperty("") var i

我是第一次在Swift中使用ReactiveCocoa的初学者。我正在构建一个显示电影列表的应用程序,我正在使用MVVM模式。我的ViewModel如下所示:

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
                }
        }

    }
}
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
}

这是活性可可的正确方法吗?我是否需要将标题和描述声明为可变的,还是仅将图像声明为唯一更改的。我想我可以使用绑定,但我不确定如何继续。

要使用反应式Cocoa+MVVM模式实现这一点,我首先将配置单元的所有逻辑从其viewmodel移动到单元类本身。然后从viewModel中删除可变属性,它们实际上是不可变的,我们不需要这些信号。对于映像,公开一个信号生成器,它将在调用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
          }
        }
  }
}

为了使用Cocoa+MVVM模式实现这一点,我首先将配置单元的所有逻辑从其viewmodel移动到单元类本身。然后从viewModel中删除可变属性,它们实际上是不可变的,我们不需要这些信号。对于映像,公开一个信号生成器,它将在调用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
          }
        }
  }
}