I am trying to populate a page view controller with 5 events that I am pulling from an API. The idea is that the user can page through the top 5 latest events and click on them to see more details about the event. The issue I am running into is that once I get the events I want to reload the view controller for the page view controller but when I try setting up the view controllers I get an error: fatal error: unexpectedly found nil while unwrapping an Optional value
. I set a property observer for my array of events so that once it's set I reload the view controllers. This is how my class looks like:
class EventPageViewController: UIPageViewController {
var eventViewControllers = [EventViewController]()
var events: [Event] = []{
didSet {
self.reloadViewControllers()
}
}
func reloadViewControllers(){
self.dataSource = nil
self.dataSource = self
// Get top 5 events
let topFiveEvents = Array(events.prefix(5))
print(self.events.count)
self.eventViewControllers.removeAll()
print(eventViewControllers.count)
// Set up view controllers
for event in topFiveEvents{
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "event") as? EventViewController{
print(event.title)
// Just this line alone is what crashes the app
controller.eventTitle.text = event.title
self.eventViewControllers.append(controller)
}
}
if self.eventViewControllers.count != 0 {
let first = [self.eventViewControllers[0]]
self.setViewControllers(first, direction: .forward, animated: false, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let timeMin = GTLRDateTime(date: Date()).rfc3339String
let params = ["maxResults": "250",
"singleEvents": "true",
"timeMin": timeMin]
NetworkManager.events(forPark: .all, withParameters: params, query: nil) { (events, error) in
if error == nil {
DispatchQueue.main.async {
self.events = events
}
}
}
}
override func viewDidLayoutSubviews() {
for subView in self.view.subviews {
if subView is UIScrollView {
subView.frame = self.view.bounds
} else if subView is UIPageControl {
let pageControl = subView as! UIPageControl
pageControl.currentPageIndicatorTintColor = UIColor(red:0.00, green:0.15, blue:0.29, alpha:1.0)
pageControl.pageIndicatorTintColor = UIColor(red:0.00, green:0.15, blue:0.29, alpha:0.30)
self.view.bringSubview(toFront: subView)
}
}
super.viewDidLayoutSubviews()
}
}
If I don't set anything in the view controller then I get the page view controller to show view controllers with static information. I'm not sure what I'm doing wrong.
It looks to me like your page view controller is trying to manipulate it's child view controllers' views directly. Don't do that.
This line:
controller.eventTitle.text = event.title
Is likely the cause, assuming that eventTitle
is an outlet.
Instead of doing that, add a string property to your child view controllers:
public var eventTitleString: String
And then set that instead:
controller.eventTitleString = event.title
And in your child view controller, add code to copy that value into your field:
func viewWillAppear(_: animated: Bool) {
super.viewWillAppear(animated)
eventTitle.text = eventTitleString
}
@MrSaturn suggests calling controller.loadViewIfNeeded()
, which would likely fix your crash, but it's bad practice. You should treat a view controllers' views as private, and never try to manipulate them from outside. Instead add a public property or function that updates your view controller's views directly.