I run into a layout problem in iOS 7:
To reproduce create a simple master-detail-app and insert this line in MasterViewController.m :
self.navigationItem.prompt = #"Master";
and this in DetailViewController.m :
self.edgesForExtendedLayout = UIRectEdgeNone;
Both lines in viewDidLoad.
The detail view's frame does not update correctly when the navigation bar shrinks to its normal size.
How should I fix this?
My current solution to this is to remove the prompt in the master view's viewWillDisappear:
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.navigationItem.prompt = nil;
}
Then just set it again in the viewWillAppear. There should be a better method, however.
Related
You can see on the gif below that on the first scroll of UITableView cell's content moves a tiny bit. You can barely see it, margins become 1 pixel wider.I've never encountered this before. It seems like there's some layout issue before the first scroll and it resolves itself after the fact. There's no warning in XCode, these custom cells are pretty straightforward, with no layout code overrides.
I don't know where to start, how do I catch this glitch?
UPDATE. I've implemented an obvious workaround for now:
- (void)scrollTableToFixGlitch {
[self.tableView setContentOffset:CGPointMake(0, 1)];
[self.tableView setContentOffset:CGPointMake(0, -1)];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self scrollTableToFixGlitch];
}
Still looking into the problem. I've tried generic UITableViewCells, nothing changed. Seems like it's the problem with View Controller's root view or tableview layout, and not with the table cells.
UPDATE 2.
I ruled out all the animations out of the question, problem lies somewhere in a different region. The glitch is easy to recreate on a much simplified project. My Tab Bar controller is based on MHCustomTabBarController with custom segues and some other additions. Here's what you do to recreate a glitch. Setup a project where your initial VC is embedded in Navigation Controller. The next controller either MHCustomTabBarController or a subclass is pushed to the navigation stack. First visible VC in tab bar is generic Table View Controller. That's it. Glitch appears only if tab bar controller is pushed in navigation stack.
Here's some code that I think matters from tab bar controller:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![self.viewControllersByIdentifier objectForKey:segue.identifier]) {
[self.viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
[self.buttons setValue:#NO forKeyPath:#"selected"];
[sender setSelected:YES];
self.selectedIndex = [self.buttons indexOfObject:sender];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [self.viewControllersByIdentifier objectForKey:self.destinationIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:MHCustomTabBarControllerViewControllerChangedNotification object:nil];
}
And a custom segue code:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
UPDATE 3
During my research I've found that - viewWillAppear is not called the first time when child controller appears. But it's called in all subsequent times.
Maybe the scrollviews contentSize is wider than your scrollView's frame(width specifically in this case) causing scrolling for both directions.
You can either try to decrease the contentSize width to the scrollView's width and
self.scrollView.alwaysBounceHorizontal = NO;
If this doesn't work, the solution would be to disable horizontal scrolling programatically by the help of the UIScrollView delegate
self.scrollView.delegate = self;
[self.scrollView setShowsHorizontalScrollIndicator:NO];
//for the below UIScrollView delegate function to work do the necessary step in the bottom of my answer.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > 0)
scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y);
}
And in your .h file you should change the interface line to below by adding UIScrollViewDelegate
#interface ViewController : UIViewController <UIScrollViewDelegate>
You most probably know this delegate part very well but for others it might be needed:D
Original answer
Ah, I've finally found the origin of this behaviour. I was almost sure this is happening due to some of the preparation methods are not called properly. As I stated in the update 3 I've found that -viewWillAppear method is not called in TableViewController when my TabBarController is pushed to the navigation stacked. I've found a ton of coverage of this matter on SO, it's a very well known issue apparently.
I've added a simple fix just to check if I'm right in my Custom Segue:
//added this line
[destinationViewController viewWillAppear:YES];
[tabBarViewController.container addSubview:destinationViewController.view];
And it works like a charm, no flickering! Now I have to figure out a more suitable place for this call, since explicit calls to methods like this can break a lot of stuff.
Probably the best place is in -navigationController:willShowViewController:animated: method of UINavigationControllerDelegate.
Anyway, problem solved. Hope it will help someone with the same issue.
UPDATE Actually, I was not completely correct on that. -viewWillAppear is called on my tab bar controller when it's pushed to navigation stack. It's not being translated to TableViewController. So there's no need to access NavigationControllerDelegate. Simple fix to a custom segue is enough.
I'm navigating from a UITableView to a normal ViewController.
I'm displaying the navigationBar like this:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
The first time I hit a row in the UITableView, the navigationBar shows up, but the second time it remains hidden. This is not expected behavior, since viewWillAppear should work every time the view shows up.
Why is the navigationBar hidden when viewing the view for the second time?
This is because you are hiding navigation bar in viewWillAppear of table view. So it will hidden for other pushed view controllers. So in order to get rid of that you have to un hide navigation bar in view did disappear of table view.So it will work in desired manner.
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBar.hidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.navigationController.navigationBar.hidden = NO;
}
I have two views, in the first i display the log in page which has a uiimage and i hide made the navigation bar translucent and in the second i have a collection view but when i scroll the navigation bar is still translucent so... i want it to be solid in the second view.
i put in the viewdidload from the second view this:
[super viewDidLoad];
self.edgesForExtendedLayout = UIRectEdgeNone;
but when i go to the second view the uiimage of the first view is late to disappear.
what ca i do?
on the first view
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = YES;
}
on the second view
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = NO;
}
I'm creating a slidingView controller using ECSlidingViewController like the one the previous version of Facebook had. Everything works fine but the menuViewController (tableViewController) is being overlapped by the status bar when I add an image to cell like shown below. Does anyone know how to fix it.
it's works 100%
- (void)viewDidLoad
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[self.tableView setContentInset:UIEdgeInsetsMake(20, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right)];
}
[super viewDidLoad];
// Initialize table data
}
Use this to set tableview frame
[self.tableView setFrame:CGRectMake(self.tableView.frame.origin.x, 20, self.tableView.frame.size.width,self.tableView.frame.size.width)];
My app is embeded in a naviguation controller, I dragged a Empty viewController then added a tool bar at the bottom.
It shows in my storyboard, but when I run on a device/simulator it does not show in the iPad.
I did the same thing on iPhone and it shows.
How do I solve this?
Here's the code that loads:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationItem setHidesBackButton:YES animated:YES];
self.hidesBottomBarWhenPushed = YES ;
}
I know it's strange to have self.hidesBottomBarWhenPushed = YES ; but my understanding is that the navigation controller has its own bottom bar. And the same code works fine on iPhone.
So im not sure why I still get an issue.
When I set it to is Initial view controller then it shows.
I fixed this issue adding the transculent bar to the viewDidLoad
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationController.navigationBar.translucent = YES;
}
Hope this helps anyone encountering this issue.