在Swift 3中设置协议的正确方法

时间:2017-03-11 12:07:46

标签: ios swift

我有一个视图控制器,可以显示来自Flickr的图像。我将Flickr API请求方法放在一个单独的类“FlickrAPIRequests”中,现在我需要在获取数据时更新视图控制器中的图像。 我选择使用协议来消除两个类的耦合。我将如何实现这一目标?

3 个答案:

答案 0 :(得分:2)

您可以定义协议并设置FlickrAPIRequests类以接受委托。我建议采用另一种方法。

FlickrAPIRequests设置为具有完成处理程序的方法。它可能看起来像这样:

  func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) 

FlickrAPIRequests函数将获取要下载的文件的URL,以及文件下载后要执行的代码块。

您可以像这样使用该功能(在本例中,该类称为DownloadManager)

DownloadManager.downloadManager.downloadFileAtURL(
  url,

  //This is the code to execute when the data is available
  //(or the network request fails)
  completion: {
    [weak self] //Create a capture group for self to avoid a retain cycle.
    data, error in

    //If self is not nil, unwrap it as "strongSelf". If self IS nil, bail out.
    guard let strongSelf = self else {
      return
    }

    if let error = error {
      print("download failed. message = \(error.localizedDescription)")
      strongSelf.downloadingInProgress = false
      return
    }

    guard let data = data else {
      print("Data is nil!")
      strongSelf.downloadingInProgress = false
      return
    }

    guard let image = UIImage(data: data) else {
      print("Unable to load image from data")
      strongSelf.downloadingInProgress = false
      return
    }

    //Install the newly downloaded image into the image view.
    strongSelf.imageView.image = image
  }
)

我在Github上有一个名为Asyc_demo(链接)的示例项目,它使用了一个简单的下载管理器,如上所述。

使用完成处理程序可以让您在调用中放置处理已完成下载的代码以开始下载,并且该代码可以访问当前作用域,以便它可以知道在图像数据被放置到何处。被下载了。

使用委托模式,您必须在视图控制器中设置状态,以便它记住正在进行的下载,并知道在完成后如何处理它们。

答案 1 :(得分:1)

编写像这样或类似的协议:

protocol FlickrImageDelegate: class {
    func displayDownloadedImage(flickrImage: UIImage)
}

您的视图控制器应该符合该协议,即使用协议方法(可以有可选方法),如下所示:

class ViewController:UIViewController, FlickrImageDelegate {

     displayDownloadedImage(flickrImage: UIImage) {
          //handle image
     }
}

然后在FlickrAPIRequests中,你需要有一个这样的委托属性:

 weak var flickrDelegate: FlickrImageDelegate? = nil

在实例化FlickrAPIRequests时将其用于视图控制器,将其实例flickrDelegate属性设置为视图控制器,在图像下载方法中,当您下载图像时,将其称为:

self.flickrDelegate.displayDownloadedImage(flickrImage: downloadedImage)

您可以考虑在FlickrAPIRequests中使用回调块(闭包),并在您咀嚼之后,查看FRP,承诺等:) 希望这有帮助

答案 2 :(得分:0)

我跟着沉默的回答,除了它对我不起作用之外它很棒。我需要将FlickrAPIRequests类设置为单例,所以这就是我所做的:

protocol FlickrAPIRequestDelegate{

 func showFlickrPhoto(photo: UIImage) }

类FlickrAPIRequests {

private static let _instance = FlickrAPIRequests()

static var Instance: FlickrAPIRequests{
    return _instance
}

var flickrDelegate: FlickrAPIRequestDelegate? = nil

// Make the Flickr API call
func flickrAPIRequest(_ params: [String: AnyObject], page: Int){

然后在按下搜索按钮的视图控制器中:

        // Set flickrDelegate protocol to self
    FlickrAPIRequests.Instance.flickrDelegate = self

    // Call the method for api requests
    FlickrAPIRequests.Instance.flickrAPIRequest(paramsDictionary as [String : AnyObject], page: 0)

以下是视图控制器符合协议的扩展名:

 extension FlickrViewController: FlickrAPIRequestDelegate{ 

func showFlickrPhoto(photo: UIImage){

    self.imgView.image = photo
    self.prepFiltersAndViews()
    self.dismissAlert()
}

}

当api方法返回照片时,我在主线程中调用协议方法:

// Perform this code in the main UI
                    DispatchQueue.main.async { [unowned self] in
                        let img = UIImage(data: photoData as Data)
                        self.flickrDelegate?.showFlickrPhoto(photo: img!)
                        self.flickrDelegate?.setPhotoTitle(photoTitle: photoTitle)
                    }