从 swift 中单独的 class 访问 label.text

Access label.text from separate class in swift

我有一个 viewController,带有一个按钮,允许用户从 url 下载文件。 为了保持我的代码干净,我创建了一个下载 class,我从我的视图中调用它。它运行良好,但现在我想为用户提供一些有关下载的 UI 提示。

假设我有一个标签,我想在下载结束后将其文本更改为 "Downloaded"。应该怎么做?

这是我的代码:

文件视图控制器

@IBOutlet weak var downloadLbl: UILabel!

func downloadFile(sender:UIButton!)
    {      
        fileDownloader().download_zip(datastring, destination: path, name: naming, fileis: self.fileId)
    }

(这只是重要的部分)

然后我将下载功能放在一个单独的 class 文件中,然后启动 DownloadTaskSession 并使用以下委托来观察下载完成

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        println("session \(session) has finished the download task \(downloadTask) of URL \(location).")

        FileViewController().downloadLbl.text = "downloaded"
    }

最后一行:FileViewController().downloadLbl.text = "downloaded" returns 错误:致命错误:在展开可选值时意外发现 nil。

有人可以帮忙吗?

结论

在 classed 之间创建通信时,两个提议的解决方案都有效,但我选择了通知解决方案,因为在我的情况下,我必须更新 UI 以及后台线程进程并使用协议没有按预期工作。已进行通信,但更新 UI 并非一直有效,即使在使用 dispatch_async 方法将更改推回主线程后也是如此。

使用通知系统很有帮助,因为它更易于实施、管理和用于 UI 修改。

FileViewController().downloadLbl.text = "downloaded"

是指"create new FileViewController, and set downloadLbl text".

您需要将文本设置为现有的 FileViewController。
要将来自 URLSession 完成块的消息告知 FileViewController,您可以使用委托或通知模式。

例如,通知模式:

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
    println("session \(session) has finished the download task \(downloadTask) of URL \(location).")

    // notify download complete!
    let defaultCenter = NSNotificationCenter.defaultCenter()
    defaultCenter.postNotificationName("CompleteDownloadNotification",
    object: nil,
    userInfo: nil)
}

----------------

@IBOutlet weak var downloadLbl: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()

    // ready for receiving notification
    let defaultCenter = NSNotificationCenter.defaultCenter()
    defaultCenter.addObserver(self,
        selector: "handleCompleteDownload",
        name: "CompleteDownloadNotification",
        object: nil)
}

func handleCompleteDownload() {
    // if notification received, change label value
    downloadLbl.text = "downaloded"
}

func downloadFile(sender:UIButton!) {      
    fileDownloader().download_zip(datastring, destination: path, name: naming, fileis: self.fileId)
}

更多详情:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/

编码愉快:)

在此代码中:

FileViewController().downloadLbl.text = "downloaded"

您正在创建一个新的 FileViewController,而不是引用现有的。这个新的还没有标签,因为它的视图还没有建立。你引用 downloadLbl,这是一个 UILabel!。它是 nil,所以您的应用程序崩溃了。

I have my download function inside a separate class file

这是一个很好的做法,但是让这个单独的 class 修改 UI 是一个不好的做法。相反,您应该编写一个 Swift protocol 来定义这些 class 是如何交互的。

一个简单的例子:

protocol MyAwesomeDownloadResponder : class {
    func downloadFinished()
}

然后你可以在你的视图控制器中实现这个方法:

class FileViewController : UIViewController, MyAwesomeDownloadResponder {
    @IBOutlet weak var downloadLbl: UILabel!

    func downloadFinished() {
        downloadLbl.text = "Downloaded"
    }
}

在您的下载 class 中,存储对 responder 的引用,然后在下载完成时调用响应程序:

class Downloader {
    weak var responder : MyAwesomeDownloadResponder?

    init(responder : MyAwesomeDownloadResponder) {
        self.responder = responder
    }

    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        responder?.downloadFinished()
    }
}

如果您需要在这些 class 之间传递任何信息,请在 protocol.

中定义它