11

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

jszumski
  • 7,430
  • 11
  • 40
  • 53
Bavan
  • 1,021
  • 1
  • 10
  • 24
  • Page view controllers allow you to turn the page either by swiping or tapping near one edge. I'm guessing that you're hitting that tap gesture recognizer on the left side. – rdelmar Feb 21 '13 at 03:58
  • I dont think so. Please have a look at attached project https://dl.dropbox.com/u/62559842/PageViewcontrollerTest.zip – Bavan Feb 21 '13 at 04:19
  • Yeah, I see what you mean. It has something to do with using the scroll transition. If you change it to curl, it behaves normally. – rdelmar Feb 21 '13 at 04:47
  • Yes rdelmer, I tried that earlier and had the same doubt. But, I have to use scrolling animation for a project I am working on. There should be a solution out there, which I am unable to find. Thanks for giving it a try anyway. Appreciate it. – Bavan Feb 21 '13 at 04:52
  • Well, good luck, but I have a feeling that this is another bug with the UIPageViewController. I tried putting a delayed call to textField becomeFirstResponder, and that caused the same behavior without even touching the screen. – rdelmar Feb 21 '13 at 05:24
  • I'm having the same problem with a UITextView. did you ever resolve this? – elsurudo Feb 27 '13 at 16:23
  • solved, see my answer. It's a bug. Need to embed the UITextView inside an UIScrollView. Didn't found the exact reason by now... – LombaX May 05 '13 at 21:23

3 Answers3

7

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.

LombaX
  • 17,265
  • 5
  • 52
  • 77
  • Yes. It works. :) Thanks. Anyway, I have no clue about what could have led you to find this solution. Thanks again. – Bavan May 06 '13 at 03:18
1

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

user
  • 3,388
  • 7
  • 33
  • 67
0

you did not set before and after view controllers and also look in to first responder for socController

Prabhat Kasera
  • 1,129
  • 11
  • 28