Search code examples
iosobjective-cswiftuipageviewcontroller

The next page in my UIPageViewController is only loading partially when the page changes


I have a UIPageViewController PageVC containing 6 view controllers I created in my storyboard. My PageVC class is written in Swift.

class PageVC: UIPageViewController, UIPageViewControllerDelegate {

    lazy var VCArr: [UIViewController] = {
        return [self.VCInstance(name: "OB1"),
                self.VCInstance(name: "OB2"),
                self.VCInstance(name: "OB3"),
                self.VCInstance(name: "OB4"),
                self.VCInstance(name: "OB5"),
                self.VCInstance(name: "OB6")]
    }()

    private func VCInstance(name: String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)

    }

    override public func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        if let OB1 = VCArr.first {
            setViewControllers([OB1], direction: .forward, animated: true, completion: nil)

        }
    }

    public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?{
        guard let viewControllerIndex = VCArr.index(of: viewController) else {
            return nil
        }

        let previousIndex = viewControllerIndex - 1

        guard previousIndex >= 0 else {
            return VCArr.last
        }

        guard VCArr.count > previousIndex else {
            return nil
        }

        return VCArr[previousIndex]
    }

    public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?{
        guard let viewControllerIndex = VCArr.index(of: viewController) else {
            return nil
        }

        let nextIndex = viewControllerIndex + 1

        guard nextIndex < VCArr.count else {
            return VCArr.first
        }

        guard VCArr.count > nextIndex else {
            return nil
        }

        return VCArr[nextIndex]
    }

    public func presentationCount(for pageViewController: UIPageViewController) -> Int{

        return VCArr.count

        }

    public func presentationIndex(for pageViewController: UIPageViewController) -> Int{

        guard let OB1 = viewControllers?.first,
            let OB1Index = VCArr.index(of: OB1) else {
                return 0

        }

        return OB1Index
    }

    public func nextPageWithIndex(index: Int)
    {
        let nextVC = VCArr[index]
        setViewControllers([nextVC], direction: .forward, animated: true, completion: nil)

    }


}

In each of the contained view controllers I have buttons that advance to the next index of PageVC. Most of the contained view controllers have classes written in Objective-C so I called the next page method like this:

-(void)pageNext {

    PageVC *vc = (PageVC *) self.parentViewController;
    [vc nextPageWithIndexWithIndex:2];

}

The transition above works great, and OB3 loads completely. In OB3 I have an IBOutlet to a button that presents my notification permissions dialog with my next page method called in the completion handler like this.

- (IBAction)enableBtn:(id)sender {

    UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {

                              [self pageNext];

                              [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notification"];

                              [[NSUserDefaults standardUserDefaults] synchronize];

                              FIRUser *user = [[FIRAuth auth] currentUser];
                              NSString *topic = [@"/topics/ios_user_" stringByAppendingString:user.uid];
                              [[FIRMessaging messaging] subscribeToTopic:topic];

                          }];

    [[UIApplication sharedApplication] registerForRemoteNotifications];

}

The page transitions smoothly when I tap "Allow" in the notification permissions dialog, but some of the elements on OB4 don't load for almost 10 seconds after the transition happens. For example, buttons appear but the text within isn't visible, and neither are UILabels. If I wait about 10 seconds they do appear. I've tried rearranging the pages in the PageVC index, but no matter which page I use the page always appears partially loaded. There are no animations attached to the elements that aren't loading properly. I think it's something to do with the completion handler in the permissions dialog. How can I make the next page in my UIPageViewController load completely upon transition?


Solution

  • Sounds like you are updating the UI from a non-main thread. Try putting the [self pageNext] inside a dispatch to the main queue:

    dispatch_async(dispatch_get_main_queue(), ^{ //update UI in main thread.
        [self pageNext]
    });