问题
Okay, I am trying to work out a pageview slider that retrieves images url from an API using this tutorial as a base.
I have already coded the asynchronous downloading of images and appending it to the pageImages array. That part works fine.
However, I received an error when I am trying to run the app in the simulator :
fatal error: unexpectedly found nil while unwrapping an Optional value
For some reason, fetching the JSON objects and populating the pageImages Array happens after I call my helper method viewControllerAtIndex.
my code
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageImages = NSArray()
var jsonData = NSArray()
override func viewDidLoad() {
super.viewDidLoad()
###THIS EXECUTES AFTER MY HELPER METHOD viewControllerAtIndex
self.fetchPublishers()
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound) {
return nil
}
index++
if (index == self.pageImages.count) {
return nil
}
return self.viewControllerAtIndex(index)
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageImages.count == 0) || (index >= self.pageImages.count)) {
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.pageIndex = index
return vc
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.pageImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
func fetchPublishers () {
var req:Request = Alamofire.request(OauthToken.Router.ReadPublishers(""))
var aClosure = {(req:NSURLRequest, response:NSHTTPURLResponse?, JSON:AnyObject?, error:NSError?) -> Void in
if (error != nil) {
println(error)
println("Couldn't connection to server.")
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
self.jsonData = JSON as! NSArray!
var baseUrl: String = "http://0.0.0.0:3000"
self.pageImages = JSON!.valueForKey("photo_url") as! NSArray!
###Prints/executes after
println(self.pageImages)
}
}
}
req.responseJSON(aClosure)
}
}
ContentViewController
class ContentViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
var pageIndex: Int!
var imageFile: String!
override func viewDidLoad() {
super.viewDidLoad()
####THE ERROR HAPPENS HERE
self.imageView.image = UIImage(named: self.imageFile)
}
}
How can I fix this?
回答1:
Things are happening out of sequence because, to quote from the AlamoFire github page: "Networking in Alamofire is done asynchronously." That means that the code in aClosure
is not executed until the response is received from the server, but execution of your viewDidLoad
carries on regardless.
The consequence is that, when startVC
is set by calling viewControllerAtIndex
, the pageImages array is empty, and return ContentViewController()
is executed. This instantiates an empty content view controller and, since it is not linked to the storyboard, the outlet imageView
is nil (and indeed imageFile
is nil). This is why you get the error.
So you need to amend that if statement in viewControllerAtIndex
to display something more helpful if the array is empty - perhaps a view controller with a message to the user that the data is downloading, and a visual indicator that the system is working.
You then have to find a way to reload the page view controller when the data does arrive from the server. To do that, you should add some code at the end of your aClosure
. In the tutorial, there is a function restartAction
which does the necessary, so either call that, or copy the code into the closure.
Finally, because that last step involves UI updates, it has to run on the main queue. So you need to amend your dispatch_async
call (in the closure) to use the main queue, not the global queue. See this answer for a full explanation on that.
来源:https://stackoverflow.com/questions/29713692/api-json-uipageviewcontroller-optional-nil