14

I've hit a weird problem with UITabBarController on iOS7 and can't seem to find a workaround, so any help would be welcome!

Scenario:

  • Navigation-based app using landscape orientation on iPad.
  • App consists of a main view, and a second view which is a UITabBarController.
  • TabBarController has two tabs.
  • First view has two buttons - each button performs a segue to the tab bar controller and sets a different tab as selected. (i.e. button1 selects the first tab, and button2 selects the second tab).
  • Setting the tab is done in prepareForSegue by calling setSelectedIndex on the tab bar controller.

Simple storyboard that demonstrates the issue

Outcome:

On iOS 7 I am finding that the view shown in the tab bar controller fails to register any touch events along the right-hand edge of the view! So in the storyboard shown above, the UISwitch on the right side of the screen cannot be tapped.

I've even attached a tap gesture recognizer to the views and used it to log the area of the screen that can be touched - it seems to register touch events up to about x=770 points across. The remaining 1/4 of the screen is 'untouchable'!

After the segue, if you manually switch to the other tab and switch back again, the touch events are 'fixed' and the full view responds to touches again.

This doesn't seem to be a problem on iOS 5 / 6.

Any help much appreciated as to:

  1. What is causing this to happen in the first place (iOS7 bug / change?)
  2. How else can I work around this? I've tried calling setSelectedViewController as well as using setSelectedIndex and this seems to be the same.

Thanks in advance.

brainjam
  • 18,863
  • 8
  • 57
  • 82
John Martin
  • 1,502
  • 1
  • 14
  • 25
  • Have you tried using something like Reveal App or Spark Inspector to see if there is anything weird going on in the view hierarchy? – Eric Allam Oct 03 '13 at 15:21
  • I'm seeing the same issue here. My guess is it is somehow related to frame/bounds not getting set up correctly in the orientation. – shawnwall Oct 04 '13 at 15:04
  • Same problem here: http://stackoverflow.com/questions/18923674/ios-7-when-rotating-view-in-tab-bar-right-side-of-view-is-not-clickable – IlDan Oct 15 '13 at 09:24

5 Answers5

13

I ended up raising this with Developer Tech Support, and it looks like a bug. This is the response I got back from Apple:

The container view that the tab bar controller sets up to contain your view controller is not being resized to account for the interface being in landscape orientation. It's dimensions at the time your view controller is displayed are 768 (width) x 1024 (height).

The view hierarchy looks like this when the selected tab's view is displayed:

UIWindow
    /* Navigation Controller */
    UILayoutContainerView
        UINavigationTransitionView
            UIViewControllerWrapperView
                /* Tab bar controller */
                UILayoutContainerView
                    UITransitionView
                        UIViewControllerWrapperView <-- Incorrectly sized.
                            /* MyViewController */
                            MyViewController.view

The incorrect size of UIViewControllerWrapperView does not cause a display problem because subviews are still displayed even if they are outside their superview's bounds. However, event routing is much more strict. Events on the right quarter of the screen are never routed to your view controller's view because the hit test fails at the wrongly-sized UIViewControllerWrapperView where the event falls outside UIViewControllerWrapperView's bounds.

As a workaround, I subclassed UITabBarController, and added the following in viewWillAppear:

@implementation FixedIOS7TabBarController

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // Fix the frame of the UIViewControllerWrapperView
    self.selectedViewController.view.superview.frame = self.view.bounds;
}

@end

Hope that helps someone else....

John Martin
  • 1,502
  • 1
  • 14
  • 25
  • I have the same problem. Tried your workaround but does not work for me. This worked for me: http://stackoverflow.com/a/19185417/765786 – Sirio Oct 11 '13 at 21:03
  • +1 for diagnosing the problem and giving such a clear explanation. Your workaround did not work in my case, but a slight modification did: http://stackoverflow.com/a/19407311/242848 – brainjam Oct 16 '13 at 15:22
10

As explained in this answer,

The container view that the tab bar controller sets up to contain your view controller is not being resized to account for the interface being in landscape orientation. Its dimensions at the time your view controller is displayed are 768 (width) x 1024 (height).

I was encountering this problem when the TabBarController was originally displayed in portrait mode. When the device was rotated into landscape mode, the view was unresponsive on the right hand side.

The solution proposed in that answer did not work for me, because viewWillAppear: is invoked only once. However, viewDidLayoutSubvews is invoked whenever the view changes, including rotations, so my solution was to subclass UITabBarController and perform the workaround in viewDidLayoutSubvews:

@implementation FixedIOS7TabBarController

- (void)viewDidLayoutSubviews
{
    // fix for iOS7 bug in UITabBarController
    self.selectedViewController.view.superview.frame = self.view.bounds;
}

@end
Community
  • 1
  • 1
brainjam
  • 18,863
  • 8
  • 57
  • 82
3

End up finding a workaround here:

self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Community
  • 1
  • 1
shawnwall
  • 4,549
  • 1
  • 27
  • 38
  • I couldn't get this to work. Which view controller / method did you put this code into? – John Martin Oct 07 '13 at 11:20
  • I put this in the root view controller one one of the tab bar items, or specifically the one that was showcasing the bad frame/bounds issue. – shawnwall Oct 08 '13 at 15:39
  • I placed only second line in my `TabController` in method `viewDidLoad` and it worked nicely. I had stranger behavior, my view was also moved up and right by 124 points. I prefere this solution since it is less intrusive then changing frame of super view. – Marek R Jan 24 '14 at 16:41
1

Right answer don't worked for me, cause user can change orientation; And it still not touchable in some area when change orientation.

So I create my own solution, I don't sure that is normal solution.

@implementation FixedIOS7TabBarController
- (UIView*)findInSubview:(UIView*)view className:(NSString*)className
{
    for(UIView* v in view.subviews){
        if([NSStringFromClass(v.class) isEqualToString:className])
            return v;
        UIView* finded = [self findInSubview:v className:className];
        if(finded)
            return finded;
    }
    return nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    UIView* wraperView = [self findInSubview:self.view className:@"UIViewControllerWrapperView"];
    wraperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
@end

Works perfectly for me!

Gralex
  • 4,285
  • 7
  • 26
  • 47
0

In the list of view controllers on the left hand side navigate to the views/view controllers affected, drag the view to underneath the first responder so that it is disassociated to the view controller's view.

Then go to the layout tab on the right hand side, select all 4 anchors and both sets of resizing arrows (horizontal + vertical).

Then drag the view back to where it was originally (just below the view controller).

James Newbould
  • 136
  • 1
  • 7