Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewControllers with TextViews in UIPageViewController

I was trying to learn UIPageViewControllers and hit an Issue which I couldn't resolve. This is what I tried to do:

  • Steps:
    1. I simply created 2 view controllers and a page view controller in StoryBoard.
    2. Then I added some code to the File's Owner of PageViewController to behave as a dataSource and delegate to itself.
    3. When I ran, things worked well.
    4. I added some buttons, and text fields to the second view controller.
    5. I ran, worked well.
    6. Now I added a text view to the second view controller and ran. When I tried to write something inside the text view, the page control jittered and moved to first view controller.

Has anyone experience this ever?

@interface AMPageViewController : UIPageViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>

@end

The implementation:

#import "AMPageViewController.h"

@interface AMPageViewController ()
{
    UIViewController *mainController;
    UIViewController* socController;
}
@end

@implementation AMPageViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                             bundle: nil];

    mainController = (UIViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: @"First"];
    socController = (UIViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: @"Second"];
    [self setViewControllers:@[mainController]
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:NO
                  completion:nil];
    self.dataSource = self;
    self.delegate = self;
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    if (viewController == socController )
        return mainController;
    else return nil;
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    if (viewController == mainController )
        return socController;
    else return nil;
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
    return 2;
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
    return 0;
}


@end

If you want to download and try the project

like image 671
Bavan Avatar asked Feb 21 '13 01:02

Bavan


2 Answers

I've investigated a lot on this problem. It seems a bug related to the internal (private) UIScrollView of the UIPageViewController. If you search on StackOverflow you will find a lot of post with this problem and no solutions...

I seems that the UITextView (which is an UIScrollView and, AFAIR, has an internal UIWebView), sends some strange message to it's superviews chain, that makes the private UIScrollView of the UIPageViewController scrolling to the top-left corner.

I would have tried to block this message using method swizzling, but this is probably not ok for AppStore. So I tried other things.

The final solution is very simple: simply, embed your UITextView inside an UIScrollView!

This is a link to your project updated

If you do so, you'll solve the problem!

Try and let me know

EDIT:

How did I arrive to this solution:

An intuition.

A lot of debug and stack traces had make me think that the problem was related to a bug in the "nesting UIScrollView" system and some messages sent from the inner view to its superview.

UITextView inherits from UIScrollView and has inside an UIWebDocumentView (private) which is another UIScrollView. During the debug I saw a lot of messages (private methods) like "relayout superview" sent to the upper UIScrollView's. So, for some reason, the inner scroll view (UIWebDocumentView?) was sending a message/event to it's superview. This message/event (probably because of a bug) was not stopping to the external UITextView, and was forwarded to the UIScrollView handled by UIPageViewController.

Embedding the UITextView inside a simple UIView was not enough, because UIView forward the message to it's superview if it can't handle. I thought: UIScrollView probably doesn't (otherwise it wouldn't simple to nest UIScrollViews), so I tried and it worked.

This is all a supposition because I stopped inspecting, I will have a more in-depth look this week.

like image 65
LombaX Avatar answered Oct 28 '22 15:10

LombaX


Build target iOS-7.0.

The scrollview trick wasn't working for me. Tried to embed the textview in a scrollview through storyboard and code but no luck.

Simply delaying the call to the textview did it. Not very elegant, but its the only thing I've gotten to work so far.

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.textView becomeFirstResponder];
    });
}

Tested, working on my iPhone 5 and my ultra-slow iPhone4. Although its totally possible that whatever implementation detail enables the textview to become the responder could take longer than the set time. So keep in mind this isn't exactly bulletproof.

--EDIT--

Well... it's working on my iPhone 4 beater with a delay of 0.0000000000000001

like image 44
user Avatar answered Oct 28 '22 14:10

user