问题
I am getting a call from a remote push notification, which includes a path to an image, which I want to display.
The AppDelegate is:
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
}
...and the view looks like this:
func loadImage(url:String) {
downloadImage(url, imgView: self.poolImage)
}
// http://stackoverflow.com/a/28942299/1011227
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!)
}
}
}
I'm getting an exception saying imgView is null (it is declared as @IBOutlet weak var poolImage: UIImageView! and I can display an image by calling loadImage("http://lorempixel.com/400/200/") from a button click.
I found a couple of related answers on stackoverflow (one is referenced above) but none work.
回答1:
If you want to show a new view controller modally, then look into rach's answer further.
If you're ViewController is already on screen, you will need to update it to show the information you need.
How you do this will depend on your root view controller setup. Is your view controller embedded in an UINavigationController? If so then try this:
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)
}
}
}
If it is not, then try this:
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)
}
}
回答2:
I believe at the point of time where you are calling
let vc = ViewController()
it has yet to be instantiated. Since you are using @IBOutlet for your UIImageView, I am assuming that you actually have that UIViewController.
Maybe it could be corrected by this:
let vc = self.storyboard?.self.storyboard?.instantiateViewControllerWithIdentifier("ViewControllerStoryboardID")
vc.loadImage("imageUrl")
self.presentViewController(vc!, animated: true, completion: nil)
Let me know if it works. :)
EDIT: You could call an instance of storyboard via this method:
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()
回答3:
By calling @IBOutlet weak var poolImage: UIImageView! you're passing the initialization of poolImage to the storyboard. Since you're initializing the view controller with let vc = ViewController(), vc will know that it is expected to have a reference to poolImage because you declared it in the class, but since you aren't actually initializing vc's view (and subviews) poolImage remains nil until the UI is loaded.
If you need to reference a UIImageView at the time of the initialization of vc, you'll have to manually instantiate it: let poolImage = UIImageView()
EDIT: Your ViewController implementation of poolImage currently looks like this:
class ViewController : UIViewController {
@IBOutlet weak var poolImage : UIImageView!
// rest of file
}
To access poolImage from AppDelegate via let vc = ViewController() you have to make the implementation look like this:
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
}
}
That way it poolImage is available for reference when ViewController is created.
回答4:
The reason I think is because the imageView may not have been initialized. Try the following solution.
Add another init method to your viewController which accepts a URLString. Once you receive a push notification, initialize your viewController and pass the imageURL to the new init method.
Inside the new initMethod, you can use the imageURL to download the image and set it to the imageView. Now your imageView should not be null and the error should not occur.
来源:https://stackoverflow.com/questions/36407437/how-can-i-call-a-view-controller-function-from-an-ios-appdelegate