Weird interface bug UIScrollView in UITabBarController. Reproducible by others? - ios

I get a weird interface bug with my UIScrollView and I cant figure out how to solve it. I only wrote one line of code (shown below) and it is a blank project's setup easily reproducible!
Setting:
I have a UIScrollView that contains a UISegmentedControl (since the segments of
the control are loaded dynamically, it could exceed the width of the screen and the scrollView is supposed to scroll the segmentedControl horizontally, the height of the scrollview is the same as the UISegmentedControl's).
The ViewController that contains this is embedded in a tabBar (or navigation bar, which also shows the bug). The whole thing is using Auto-Layout.
Bug:
When I scroll the SegmentedControl some degree to the right and then switch the viewController by clicking the other tab on the tabBarController, the content-offset of the segmented control gets weirdly shifted when switching back to the initial viewcontroller. When I try to scroll to the leftmost part of the scrollview it won't let me. When switching the tabs a couple of times, it gets fixed again and I can do this over.
What I did (can you reproduce this?):
Create a blank single-view ios project
Embed the already given viewController in a tabbarcontroller.
Put a scrollView on the upper portion of the view that fits the screen from left to right.
Put a UISegmentedControl on the topleft corner of the scrollview and drag the scrollview to fit the segmented controls height height
Change the Segmented control's width a bit so xcode adds a width-constraint. in the segmented control's width constraint change the width constraint's relation to "greater than or equal"
create an outlet to the segmented control
in viewDidload add this code
[self.segmentedControl insertSegmentWithTitle:#"A really long title so it you have to scroll to see it" atIndex: 0 animated: NO];
Create a blank viewcontroller and add it as a second viewController for the tabbarController.
This is how my storyboard looks like:
Now run the project, scroll the segmented control to it's right end as far as it goes. Switch the tab and switch back and please tell me how your scrollview now behaves - and WHY.
My guess would be it has something to do with Auto Layout maybe? Can't figure out what though.
I tried fixing this by setting the scrollView's contentSize in viewDidAppear or changing the content offset of the scrollView in viewDidAppear or changing frames, combination of those and what not....
Extra question:
Is it no longer neccessary to set the scrollViews contentSize property? Why does it scroll the content automatically?

After googeling I found the answer in another StackOverflow question.
What you need to do is save the scrollview.contentOffset on viewWillDisappear,
set it to CGPointZero on viewDidDisappear and set it back to the saved state on viewDidLayoutSubviews:
-(void) viewWillDisappear: (BOOL) animated {
self.lastContentOffset = self.scrollView.contentOffset;
[super viewWillDisappear: animated];
}
-(void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear: animated];
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidlayoutSubviews];
self.scrollView.contentOffset = self.lastContentOffset;
}

Related

iOS - Animations inside Navigation Bar doesn't work

I have a scenario where I needed to add an animation inside the Navigation Bar, but the simulator doesn't animate. I add a UIView, add a label/text field and create outlets for the constraints and update them with "UIView animateWithDuration:" Instead, the simulator just updates the constraints and displays the view accordingly, but without any animation. I have tried it by modifying both constraints as well as frames (separately).
[self.view layoutIfNeeded];
[UIView animateWithDuration: 3
delay: 0.0
options: UIViewAnimationOptionCurveEaseInOut
animations: ^{
self.constraint.constant += 60;
NSLog(#"inside animation");
[self.view layoutIfNeeded];
}
completion:nil];
This is shown when I increment the width constraint by "60" in viewDidLoad: . And it comes out non-animated.
When I configure the constraints (increment width by "60") through a button click, even a static updation doesn't happen.
The same code works perfectly outside the navigation bar inside the UIView, though.
Appreciate any help...
If your navigation bar contains some animation, then you could try out drawing custom navigation bar. Here's what you've to do :
In your view controller in viewDidLoad() method add the following code
self.navigationController.navigationBarHidden = YES;
After that using a UIView create a custom navigation bar with frame adjusted to be visible and top.
After that add components on the view and start working with animations.
You can achieve that by taking custom UIView and create width NSLayoutConstraint outlet to it and in code you can handle the constant property of this variable by calling UpdateConstraintsIfNeeded or UpdateViewConstraints method before calling animation code
Ok, I've solved this at last. Phew...
What I did was...
Added a view in place of barButtonItem (let's call this "superView") and over it another view (and let's call this "subView") so that I get the following configuration.
Constraints were set between subView and objects on top of it. Now, I created outlets (superView and subView) for superView and subView, made them transparent (optional, of course).
Useful note: Set the superView's width to the default bar button item's width and the subView's to the screen width. Disable "Clip Subviews" on superView but enable on the subView.
Added animations as needed where I had wanted, but layoutIfNeeded on the "superView", not the parent view.
[self.superView layoutIfNeeded];
Good luck.

UITextView content going under UINavigationBar

I have a UITextView inside a UIViewController that uses auto layout to pin it to 0 on all sides (so it fills the whole screen). I also have this view being pushed using UINavigationController.
I'm running into a weird error where if the UITextView has enough text so that it runs off the screen, then content gets set under the UINavigationBar. If there is not enough text to fill the screen the layout of the text doesn't go under the UINavigationBar.
Here is what's happening, this is when there is enough text that it goes off the screen and you need to scroll to view all of it.
I've tried:
Setting the content inset of the UITextView.
Made sure the UINavigationBar isn't translucent.
Tried setting this self.automaticallyAdjustsScrollViewInsets = NO;
Inside viewDidLoad of viewController where your textView is, add this:
self.edgesForExtendedLayout = UIRectEdgeNone;
I'm not sure why the problem was occurring but this fixed it:
- (void)viewDidLayoutSubviews {
if (self.textView) {
[self.textView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
}
}

iOS 7 UIScrollView doesn't scroll when presented as modal view controller, works fine otherwise

I have a storyboard in which I have a view controller, (InfoViewController) in which I have an UIScrollView with some labels, uitextviews, etc. this is all created in IB, no code has been written at all. The only thing that is left for me to do is to set the content size, which I do as following:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
CGRect screenRect = [[UIScreen mainScreen] bounds];
[self.scrollView setContentSize:CGSizeMake(screenRect.size.width, 600)];
[self.scrollView setBackgroundColor:[UIColor greenColor]];
}
Whenever I make this view the entry point of my app, it works perfectly. I can see my view, the content size is set, the background color is being set to green.
Now it comes, I created another view controller, and this view controller is now my entry point of the app. I added a button in there, and on this button I did a "modal segue" to the earlier mentioned Info View Controller.
When I now run my app, I press this button, my Info View Controller shows up. The green background color is being set, but it's impossible to scroll. So the code is being executed (otherwise the background color couldn't been green, in the storyboard it's just plain white) but somehow whenever I use this "modal segue", the scroll functionality gets lost.
How can I fix this?
Try to insert a UIView into the scroll view...
Set the UIView with top, bottom, leading and trailing space to super view to 0.
Then insert everything into the UIView rather than into the ScrollView
Then modify the constraint height of the inner UIView instead of the contentsize of the scroll view, it works with iOS7

Finding The Center of a View

I have an iphone app with 2 ViewControllers . Both screens(viewcontrollers) show a loading screen. I create the loading screen programmatically:
UIView *loadingScreen = [[UIView alloc] initWithFrame:CGRectMake(100,200,144,144)];
loadingScreen.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
//{.. Other customizations to loading screen
// ..}
[self.view addSubview:loadingScreen];
For some reason, the second viewcontroller's loadingScreen is significantly lower and it isn't centered on the screen. The first viewcontroller works perfectly and is dead center like I want.
The second viewcontroller is a UITableView and it shows the uinavigationbar, whereas the first viewcontroller doesn't show the uinavigationbar. Also, I use storyboard for my app.
I've outputted to the NSLog self.view.frame.size.height and loadingScreen.center in both instances and THEY HAVE THE SAME COORDINATES! So, not sure why it is showing up lower. Any ideas why the second loadingScreen is lower and how to fix? Thanks!
You mention that one screen displays a UINavigationBar while the other does not. When you display a navigation bar, it offsets the rest of your view - in this case by shifting it down.
There are two quick fixes. You can either adjust your center point up by the size of the UINavigationBar (65 pts - unless it's a custom UINavigationBar and you've changed its size) or you can set the "Adjust Scroll View Insets" value to false in the attributes inspector.
The latter is probably the easiest and comes most recommended. Note though, that the top of your UITableView will now be underneath the UINavigationBar.
My final note would be that if you wanted to do it programmatically than in your UITableView's delegate you can call
- (BOOL)automaticallyAdjustsScrollViewInsets
{
return NO;
}

iOS 7 TableView in a ViewController and NavigationBar blurred effect

I started building a TableView in my app by using a TableViewController in a storyboard. When you do this, you have a very cool effect when you scroll down your list : the cells moving behind the nav bar get blurred.
Some time later, I had to move from this TableViewController to a ViewController with a TableView inside (I had to add other views at the bottom of the table).
In order to avoid having the first cells hidden by the navigation bar (being over it), I added constraints to the Top and Bottom Layout Guides, and to the left and right edges of the view.
This works fine, but I lost the cool blurred scrolling effect : the cells seem to be disappearing before going behind the navigation bar.
I've seen workarounds with people not using constraints and putting magic numbers in interface builder. I cannot do this, first because I dislike it, and second because I have to be iOS 6 compatible.
What did I miss to be able to benefit again from the blurred navigation bar effect ?
You have to manually adjust the contentInset of the table view and make sure the table view frame origin is 0, 0.
In this way the table view will be below the navigation bar, but there will be some margin between the content and the scroll view edges (the content gets shifted down).
I advise you to use the topLayoutGuide property of the view controller to set the right contentInsets, instead of hard coding 64 (status bar + navigation bar).
There's also bottomLayoutGuide, which you should use in case of UITapBars.
Here is some sample code (viewDidLoad should be fine):
// Set edge insets
CGFloat topLayoutGuide = self.topLayoutGuide.length;
tableView.contentInset = UIEdgeInsetsMake(topLayoutGuide, 0, 0, 0);
By the way, this properties of UIViewController might help you (you should not need to change their default values, but I don't know what your view hierarchy is):
automaticallyAdjustsScrollViewInsets
edgesForExtendedLayout
extendedLayoutIncludesOpaqueBars
The tableView needs to be full screen. That is underneath the top and bottom bars. Note don't use the top and bottom layout guides as they are used for positioning relative to the bars not underneath.
Then you need to manually set the content inset of the tableview. This sets the initial scroll position to under the top bar.
Something like:
CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
CGFloat h=MIN(statusBarSize.width, statusBarSize.height);
UIEdgeInsets e = UIEdgeInsetsMake(self.navigationController.navigationBar.bounds.size.height + h,
0.0f,
0.0f,
0.0f);
self.tableView.contentInset = e;
Not you get this functionality for free when using a tableView controller and the "Automatically Adjust content inset" settings
You probably have the coordinates of your tableView not set to (0, 0) to map to those of the viewController.view.frame or viewController.view.bounds. If you have done that, try setting
self.navigationController.navigationBar.translucent = YES;
UIViewController property edgesForExtendedLayout does the trick. If you are using storyboards just make sure Extended Edges Under Top Bars is on (and it is by default).
If you are creating your view controller programmatically try this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.edgesForExtendedLayout = UIRectEdgeAll;
}
And of course, your table view needs to have proper autoresizing mask/layout constraints
edgesForExtendedLayout is not what you want here, as this will limit the table view underneath the navigation bar. In iOS 7, the view controllers uses fullscreen by default, and the property controlling where the tableview content starts is automaticallyAdjustsScrollViewInsets. This should be YES by default, so check if it is somehow set to NO, or try setting it explicitly.
Check this answer for a good explanation on how this works:
https://stackoverflow.com/a/19585104/1485715

Resources