如何从 iOS AppDelegate 调用视图控制器函数?

How can I call a view controller function from an iOS AppDelegate?

我接到了来自远程推送通知的电话,其中包括我要显示的图像的路径。

AppDelegate 是:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    // get url from notification payload (shortened version)
    let url = alert["imgurl"] as? NSString 
    let vc = ViewController()
    vc.loadImage(url as String) // cannot find the imageView inside
}

...视图如下所示:

func loadImage(url:String) {
    downloadImage(url, imgView: self.poolImage)
}

// 
func getDataFromUrl(url:String, completion: ((data: NSData?) -> Void)) {
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data, response, error) in
        completion(data: NSData(data: data!))
        }.resume()
}

func downloadImage(url:String, imgView:UIImageView){
    getDataFromUrl(url) { data in
        dispatch_async(dispatch_get_main_queue()) {
            imgView.image = UIImage(data: data!)
        }
    }
}

我收到一个异常,说 imgView 为空(它被声明为 @IBOutlet weak var poolImage: UIImageView!,我可以通过单击按钮调用 loadImage("http://lorempixel.com/400/200/") 来显示图像。

我在 Whosebug 上找到了几个相关的答案(上面引用了一个)但是 none 有效。

通过调用 @IBOutlet weak var poolImage: UIImageView!,您将 poolImage 的初始化传递给故事板。由于您使用 let vc = ViewController() 初始化视图控制器,因此 vc 将知道它应该具有对 poolImage 的引用,因为您在 class 中声明了它,但是由于您实际上并没有初始化 vc 的视图(和子视图) poolImage 在加载 UI 之前保持为 nil。

如果在初始化vc时需要引用一个UIImageView,则需要手动实例化它:let poolImage = UIImageView()

编辑:您的 ViewController 实施 poolImage 目前看起来像这样:

class ViewController : UIViewController {

    @IBOutlet weak var poolImage : UIImageView!

    // rest of file

}

要通过 let vc = ViewController()AppDelegate 访问 poolImage,您必须使实现看起来像这样:

class ViewController : UIViewController {

    /* ---EDIT--- add a placeholder where you would like 
    poolImage to be displayed in the storyboard */

    @IBOutlet weak var poolImagePlaceholder : UIView!

    var poolImage = UIImageView()

    // rest of file

    override func viewDidLoad() {
        super.viewDidLoad()

        /* ---EDIT--- make poolImage's frame identical to the
        placeholder view's frame */

        poolImage.frame = poolImagePlaceholder.frame
        poolImagePlaceholder.addSubView(poolImage)
        // then add auto layout constraints on poolImage here if necessary

    }

}

这样poolImage在创建ViewController时可以参考

我相信在你打电话的那个时间点

let vc = ViewController()

它还没有被实例化。由于您将 @IBOutlet 用于 UIImageView,因此我假设您实际上拥有该 UIViewController。

也许可以通过以下方式更正:

let vc = self.storyboard?.self.storyboard?.instantiateViewControllerWithIdentifier("ViewControllerStoryboardID")
vc.loadImage("imageUrl")
self.presentViewController(vc!, animated: true, completion: nil)

让我知道它是否有效。 :)

编辑:您可以通过此方法调用情节提要的实例:

self.window = UIWindow(frame: UIScreen.mainScreen().bounds)

var storyboard = UIStoryboard(name: "Main", bundle: nil)

var vc = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! UIViewController

vc.loadImage("imageUrl")

self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()

我认为的原因是因为 imageView 可能不是 initialized。请尝试以下解决方案。

向您的 viewController 添加另一个 init 方法,它接受 URLString。一旦您收到 push notificationinitialize 您的 viewController 并将 imageURL 传递给新的 init 方法。

在新的initMethod里面,可以使用imageURL下载图片,设置为imageView。现在你的 imageView 不应该是 null 并且错误应该不会发生。

如果您想以模态方式显示新的视图控制器,请进一步查看 rach 的答案。

如果您 ViewController 已经在屏幕上,您将需要更新它以显示您需要的信息。

如何执行此操作取决于您的根视图控制器设置。您的视图控制器是否嵌入到 UINavigationController 中?如果是这样,那么试试这个:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    if let rootViewController = window?.rootViewController as? UINavigationController {
        if let viewController = rootViewController.viewControllers.first as? ViewController {
            let url = alert["imgurl"] as? NSString
            viewController.loadImage(url as String)
        }
    }
}

如果不是,那么试试这个:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    if let rootViewController = window?.rootViewController as? ViewController {
        let url = alert["imgurl"] as? NSString
        rootViewController.loadImage(url as String)
    }
}