【问题标题】:Access label.text from separate class in swift快速从单独的类访问 label.text
【发布时间】:2015-07-29 15:44:02
【问题描述】:

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

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

这是我的代码:

文件视图控制器

@IBOutlet weak var downloadLbl: UILabel!

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

(这只是重要的部分)

然后我在一个单独的类文件中拥有我的下载功能,我启动了一个 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" 返回错误:致命错误:在展开 Optional 值时意外发现 nil。

有人可以帮忙吗?

结论

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

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

【问题讨论】:

    标签: ios swift viewcontroller


    【解决方案1】:

    在这段代码中:

    FileViewController().downloadLbl.text = "downloaded"
    

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

    我的下载功能在一个单独的类文件中

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

    一个简单的例子:

    protocol MyAwesomeDownloadResponder : class {
        func downloadFinished()
    }
    

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

    class FileViewController : UIViewController, MyAwesomeDownloadResponder {
        @IBOutlet weak var downloadLbl: UILabel!
    
        func downloadFinished() {
            downloadLbl.text = "Downloaded"
        }
    }
    

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

    class Downloader {
        weak var responder : MyAwesomeDownloadResponder?
    
        init(responder : MyAwesomeDownloadResponder) {
            self.responder = responder
        }
    
        func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
            responder?.downloadFinished()
        }
    }
    

    如果您需要在这些类之间传递任何信息,请在protocol 中定义它。

    【讨论】:

    • 我已经更新了我对您解决方案的看法。我遇到了一个我不明白的错误。
    • 我终于找到了解决这个错误的方法。但是,我注意到虽然在控制台中打印某些内容总是有效,但更改标签文本并不会一直执行......
    • @SKYnine 您可能需要在downloadFinished() 方法内的主队列中添加dispatch_async
    【解决方案2】:
    FileViewController().downloadLbl.text = "downloaded"
    

    is 表示“创建新的 FileViewController,并设置 downloadLbl 文本”。

    您需要将文本设置为现有的 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/

    编码愉快:)

    【讨论】:

    • 这看起来是处理这个问题的好方法,但我不确定它是否符合我的需要。这与问题无关,但我需要同时处理多个下载任务。这个方案会触发handleCompleteDownload而不考虑任务队列标识符?
    • @SKYnine 好的,您使用userinfo 处理多个下载任务。例如,您使用 fileId :defaultCenter.postNotificationName("CompleteDownloadNotification",object: nil,userInfo: ["fileId":fileId])
    • 好的,在 viewdidLoad 端如何检查正确的 fileId?另外,是否可以短间隔发送通知?我的意思是我可以用它来跟踪下载的字节并显示进度条吗?
    • 好的,我终于回到了你的解决方案,它效果最好。我需要进行可靠的 ui 更新,并且使用的协议无法正常工作。谢谢!
    猜你喜欢
    • 2011-11-30
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    相关资源
    最近更新 更多