UIScrollview position moves after touches begin - ios

I view imbedded in a Navigation Controller which is slightly transparent. This view contains a UIscrollview. This UIScrollview has the following subviews: UIImageView, UILabel, and UIWebview. All these other views are working correctly. When the view loads, everything is in the correct position (The views are below the Navigation Bar). However, as soon as the screen is tapped, the scrollview repositions itself to the top of the view (aka behind the Navigation Bar, which is at the top, and I can see that this is true because the Nav Bar is slightly transparent)
Any idea why this is happening? I set up the scrollview like this:
self.scrollView.frame = CGRectMake(0, self.navigationController.navigationBar.frame.size.height, self.view.window.frame.size.width, self.view.window.frame.size.height);
Also set these properties:
self.scrollView.scrollEnabled = YES;
self.scrollView.alwaysBounceVertical = YES;
self.scrollView.bounces = YES;
Anybody know why this is happening?

I'd similar problem, but only on iOS 7.0. Try to set the translucent = NO property of the navigationBar. This will make the navigationBar opaque. Although if you want the navigationBar to be transparent, try to change the contentInset of the scrollView to adjust the content as you want. Hope that this will help :)

Related

Set image view underneath the navigation bar

In my UIViewController I have UIImageView that takes up the full size of the controller (screen) and serves as a background. On top of UIImageView I have a UITableView with a clear background. I set the navigation bar to translucent like this:
self.navigationController.navigationBar.translucent = YES;
UIImageView is underneath the navigation bar as I want it to be. Unfortunately the same happens to UITableView. I want to put UITableView at the bottom of navigation bar, leaving UIImageView underneath. If I set:
[self setEdgesForExtendedLayout:UIRectEdgeBottom]
then UITableView is at the bottom of the navigation bar, but the same happens also to UIImageView. What would be the easiest solution to leave UIImageView underneath the navigation bar and push down the UITableView at the bottom of the navigation bar?
By default in iOS 7 the content extends to the top and bottom of the screen, underneath any navigation bars, tool bars, or tab bars. If you were to set the frame of the table view to be start after the navigation bar, the content of the table view would not scroll beneath the navigation bar providing the nice blur effect.
What you'll probably want to do instead, is keep the y origin of your table view at 0, underneath the navigation bar, and set the content inset so the content starts after the navigation bar.
This is pretty simple. If you're using auto layout the top layout guide of the view controller will recognise the height of the status bar and navigation bar so you don't need to calculate this yourself. The bottom of the navigation bar should end at 66.
//Using Auto Layout
CGFloat navigationBarHeight = self.topLayoutGuide.length;
//Not using Auto Layout
UINavigationBar *nav = self.navigationController.navigationBar;
CGFloat navigationBarHeight = nav.frame.origin.y + nav.frame.size.height;
myTableView.contentInset = UIEdgeInsetsMake(navigationBarHeight, 0, 0, 0)
And of course if you actually do want the frame to start after the navigation bar the height above stays the same, you just need to manually set the frame of the table view.
CGRect tableViewFrame = self.view.bounds;
tableViewFrame.origin = CGPointMake(tableViewFrame.origin.x, navigationBarHeight);
tableViewFrame.size = CGSizeMake(tableViewFrame.size.width, tableViewFrame.size.height - navigationBarHeight*2);
myTableView.frame = tableViewFrame;
EDIT: Ah, almost forgot. You'll also want to change the scrollIndicatorInsets to match the contentInset, so your scroll bars don't move offscreen.
myTableView.scrollIndicatorInsets = myTableView.contentInset;

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

UIScrollView is moving up if I hide the NavigationBar and the ToolBar

I have a problem, I created a zoomable UIImageView with a UIScrollview, everything works perfect, exept the UIImageView is not in the center anymore and every time I hide the Tool- and NavigationBar the UIImageView/UIScrollView is moving up.
I already set the autoresizingMask:
scrollView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
scrollView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin;
but it won't work, can someone please help me?
I had the same problem with iOS 7, and read I had to set
self.automaticallyAdjustsScrollViewInsets = NO;
for the view controller, but it did not work for me. After I have released I had to put it in viewDidLoad method of the parent view controller of the view controller.
Just like #Mert answered, you can either set it in code self.automaticallyAdjustsScrollViewInsets = NO; or you can set this in storyboard, in the scroll view in one of the inspectors you can un-check the checkbox that automatically adjusts the scroll view content.
EDIT
The checkbox is in the attributes inspector of the UIScrollView, it's called Autoresize Subviews under drawing section.
Btw, this behaviour only happens in iOs7, because of the transparent UINavigationBar thing.

How do I get a UINavigationController to NOT change its view size when setting the translucent property?

I have an app where up until now I've been using a UINavigationController with a UINavigationBar that has its property translucent = YES. This means the UINavigationController's content view (i.e. the views from the view controllers you push) to be full-screen (minus status bar).
However, if you set the navigationBar.translucent = NO, this container view becomes 44pt shorter, as I suppose Apple has assumed you don't need any content under an opaque navigationBar.
... except if you're doing what we're doing and are employing a navigationBar that scrolls away (see This Post on how to do that) So I'd like to know if this is possible.
I want to have translucent = NO, but have everything behave as if it were still set to YES. I like the functionality of the translucent = YES, but I don't actually want the bar to be made translucent by UIKit.
What worked for me was to add
extendedLayoutIncludesOpaqueBars = true
in
viewDidLoad
something like this
override func viewDidLoad() {
super.viewDidLoad()
extendedLayoutIncludesOpaqueBars = true
}
Hope it will work for you as well
It's not necessarily a good answer but you could just offset your view that high if you're not translucent.
//This won't take into account orientation and probably other details
if(!self.navigationController.navigationBar.isTranslucent)
{
self.view.frame = CGRectMake(0,0,-44,self.view.bounds.size.height);
}
You could put that in your viewDidLoad or viewWillAppear and if you have a bunch of view controllers you can just subclass them all and put your logic in the subclass.
I found a solution that works, although it is indeed a bit of a hack.
The idea is to give the translucent nav bar an opaque backing. Unfortunately I'm not happy with the solution in that it's dirty and not encapsulated and introduces some potential issues, but i AM happy because it got the job done.
In my Application's base view controller class (i.e. MyViewController : UIViewController), in the viewDidLoad method, I instantiate a new ivar UIView *_navigationBarBG and give it the same frame as self.navigationController.navigationBar. I then set it's backgroundColor property to [UIColor whiteColor] although this is how you achieve some more tint I guess. [EDIT:If you wanted to be a purist (color values remaining exactly as they come from the .psd), you could make the _navigationBarBG a UIImageView and use your custom background there, and the background of the actual UINavigationBar you set to draw clear (or stretch a 1px transparent image if you wanted to use a typical 'change your navigation bar using an image' recipe that's somewhere on the internet)]
if(self.navigationController)
{
_navigationBarBG = [[UIView alloc] initWithFrame: self.navigationController.navigationBar.frame];
_navigationBarBG.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_navigationBarBG];
}
THEN, (and this is the crappy part, but I don't see any other way), I add this view as a subview. BUT, whenever you would normally make a call to [self.view addSubview: anyView], you have to make sure you call [self.view insertSubview: anyView belowSubview: _navigationBarBG];
if (_navigationBarBG)
[self.view insertSubview: anyView belowSubview:_navigationBarBG];
else
[self.view addSubview: anyView];
If you forget that, these added views will slide under your navbar background and look weird. So you need to know that this is a source of error.
WHY AM I DOING THIS? Again you might ask... I want to be able to have a scrolling navigation bar that scrolls out of the way when you scroll down your table view, thereby giving the user more screen space. This is done by using the scrollView delegate (scrollViewDidScroll:) and also viewWillAppear:
// FIRST DEAL WITH SCROLLING NAVIGATION BAR
CALayer *layer = self.navigationController.navigationBar.layer;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGPoint newPosition;
if (contentOffsetY > _scrollViewContentOffsetYThreshold && self.scrollingNavigationBarEnabled) {
newPosition = CGPointMake(layer.position.x,
22 - MIN((contentOffsetY - _scrollViewContentOffsetYThreshold), 48.0)); // my nav bar BG image is 48.0 tall
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
else
{
newPosition = kNavBarDefaultPosition; // i.e. CGPointMake(160, 22) -- portrait only
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
I was looking for an answer to this as I wanted my subviews to be at (0,0) and not (0,44)(in reference to the Screen bounds), but I could not find an answer on how to set this in the NavigationController, which I thought would be an included property.
What I ended up doing that was very simple is adding a subview to the navigation controller that was the width and height of the Navigation Bar, but then insert the subview below the Navigation Bar.
Now the setting is Translucent = YES, but it still appears solid and the subviews behave how I want.
EDIT: After re-reading your original post, I suppose if you're going to be rolling the nav bar away, you'll have to take into account hiding and showing the new subview as you do the same with the nav bar

Black bar flashes at top of UITableView when pushing to view with "Hides Bottom Bar When Pushed" in IB

This is a weird error that may just be an issue in Xcode for all I know. I have a tab bar controller where the first view is a UITableView with (obviously) a number of cells. When you select a cell, I've set up a segue on the MainStoryboard to go to a detail view controller. I want the tab bar to be hidden when I go to the detail view, so I went into the storyboard, chose my detail view, and clicked "Hides Bottom Bar on Push" in the editor screen that starts with "Simulated Metrics."
Everything works just fine, except that when I tap on a cell, a black bar flashes at the top of the UITableView screen, dropping the tableview cells down (as if the cells are falling down below the tab bar at the bottom), just before the screen pushes over to the detail view. The effect isn't harmful at all, but it's very disconcerting, and I'd like to smooth that out.
The only fix I've found is to uncheck the "Hides Bottom Bar when Pushed" option on the storyboard. That indeed does get rid of that black bar flash, but of course the tab bar stays on the screen when I go to the detail view, which is what I don't want.
Any ideas?
Just for completeness' sake, I went ahead and ran
[self.navigationController setToolbarHidden:YES animated: YES];
on the detail view controller's viewWillAppear method (and even tried it with the storyboard option both on and off), but there was no difference. The toolbar did indeed hide just fine, but I still got that black line at the top. So weird.
I know it is too late !!! I ran into same issue. It seems like the Auto resizing mask for the view was incorrect to be exact the UIViewAutoresizingFlexibleTopMargin. I checked this on in the xib file. If you are trying to do it in code make sure this flag -UIViewAutoresizingFlexibleTopMargin - is not included in the autoresizing mask.
Hope this will help some one in the future
I know it is a bit late, but I have same problem and I can't solve it with any of the previous answers. (I suppose this is the reason non was accepted).
The problem is that view size of the SecondViewController is same as view size of a previous ViewController, so too small to fit in a ViewController with Toolbar hidden. Thats why black background of a UITabBarController is visible at the top when transition is happening, and on a viewDidAppear view will stretch on right size.
For me it help to subclass root UITabBarController and set background color to same background color as SecondViewController has.
class RootViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = Style.backgroundColor
}
}
Then you can leave checkbox checked inside storyboard and it will look ok.
P.S.
If you have some views, that is position on the bottom part of the view, you need to set bottom constraints so they are smaller by 49 (because this is the height of the toolbar), and then on viewDidAppear set the right constraint.
For example:
I have view that need to be position 44 px from bottom edge. Before, I have constraint set to 44 and I have some strange behaviour of that view. It was placed to height and then jump on the right place.
I fix this with setting constraint to -5 (44-49), and then in viewDidAppear set the constraint back to 44. Now I have normal behaviour of that view.
Wow I just had the same issue now, very painful, and no info on the net about it.
Anyway, a simple workaround for me was to change the current view's Frame moving the y coordinates up and making the height bigger by the height of the tab bar. This fixed the problem if done straight after pushing the new view onto the navigation controller. Also, there was no need to fix the Frame afterwards (it must be updated when the view is shown again).
MonoTouch code:
UIViewController viewControllerToPush = new MyViewController();
viewControllerToPush.HidesBottomBarWhenPushed = true; // I had this in the MyViewController's constructor, doesn't make any difference
this.NavigationController.PushViewController(viewControllerToPush, true);
float offset = this.TabBarController.TabBar.Frame.Height;
this.View.Frame = new System.Drawing.RectangleF(0, -offset, this.View.Frame.Width, this.View.Frame.Height + offset);
Objective C code (untested, just a translation of the monotouch code):
UIViewController *viewControllerToPush = [MyViewController new];
viewControllerToPush.hidesBottomBarWhenPushed = YES; viewControllerToPush.hidesBottomBarWhenPushed = YES;
float offset = self.tabBarController.tabBar.frame.size.height; float offset = self.tabBarController.tabBar.frame.size.height;
self.view.frame = CGRectMake(0, -offset, self.view.frame.width, self.view.frame.height + offset); self.view.frame = CGRectMake(0, -offset, self.view.frame.size.width, self.view.frame.size.height + offset);
Do this in viewWillAppear of detailViewController, it should work fine
subclass your navigation controller, or just find the navigation bar
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let backdropEffectView = navigationBar.subviews[0].subviews[0].subviews[0] //_UIBackdropEffectView
let visualEffectView: UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
visualEffectView.frame = backdropEffectView.frame
backdropEffectView.superview?.insertSubview(visualEffectView, aboveSubview: backdropEffectView)
backdropEffectView.removeFromSuperview()
}

Resources