iOS7 Webview initial scroll position under Navigation bar - ios7

I have a webview which is scrolling as desired underneath a navigation bar.
However, when I first load the controller, the page loaded in the webview is scrolled so that it aligns with the top of the navigation bar. When I scroll the web view, the correct inset is present at the top to sit correctly, it's just the initial position that is incorrect.
How can I get the initial position to be fully scrolled to the top, including the inset?

If you don't mind having an opaque navigation bar, then the simplest solution could be to do this in the view controller that contains your web view:
self.navigationController.navigationBar.translucent = NO;
The positioning of the frame will then adopt the same behavior as iOS6, magically!

Rick's answer is correct except the top value should be negative, this is what worked for me!
[self.webView.scrollView setContentInset:UIEdgeInsetsMake(-44, 0, 0, 0)];
[self.webView.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(-44, 0, 0, 0)];
[self.webView.scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
I used -44, if you don't have status bar on the top you should use -64.

The way I solved this issue is with the webViewDidFinishLoad delegate callback. In the callback method for the web view I set do all the work to make the web view's scroll view look correct.
[webView.scrollView setContentInset:UIEdgeInsetsMake(64, 0, 0, 0)];
[webView.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(64, 0, 0, 0)];
[webView.scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];

I had the same issue and fixed it. The solution is pretty simple:
Go to the storyboard, select the view controller which contains your UIWebView and open the Attributes Inspector. Here, you'll see the title "Extend Edges", just uncheck the box "Under Top Bars" and I will work !
Hope this helps !

You can also set the content offset of the webview's scrollview in viewDidAppear:, for example:
[self.webView.scrollView setContentOffset:CGPointMake(0, -64) animated:NO];
Unfortunately, it has no effect if placed in viewWillAppear:, so when the view appears you will see a visible jump in the content as it shifts from underneath the navigation bar to its new location.

use the below
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat navHeight = self.navigationController.navigationBar.frame.size.height;
applicationFrame.origin.y = navHeight + 4;
webView = [[UIWebView alloc] initWithFrame:applicationFrame];

My solution:
- (void)loadView {
...
_offsetSet = NO;
}
- (void) viewDidLayoutSubviews {
if (!_offsetSet) {
[_webView.scrollView setContentOffset:CGPointMake(0, -self.topLayoutGuide.length) animated:NO];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
_offsetSet = YES;
}

For a more general version of #Puran's answer, rather than hardwiring 44 or 64, get it from the topLayoutGuide. Also, if you load multiple times, you only need to change these values once:
UIEdgeInsets insets = self.myWebView.scrollView.contentInset;
if ( UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero)) {
insets.top = -self.topLayoutGuide.length;
[self.myWebView.scrollView setContentInset:insets];
[self.myWebView.scrollView setScrollIndicatorInsets:insets];
[self.myWebView.scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
}

Related

UITableView first rows are cut-off under top bar

I have a UITabBarController with two UITableViews, all were created in the storyboard.
The problem is that in the second tableview the first few lines of the table are under the top bar, this doesn't happens with the first tableview, even if I change the order of the views the first will work perfectly and the second will present the problem, so the one that was working perfectly now presents the same problem because is the second item of the tabbar controller.
I don't have much code to show because I didn't create the tableviews programatically.
Not sure exactly based on your description, but a couple possibilities come to mind:
Check your view controller attributes inspector in IB. Look for
"Extend Edges" option under View Controller, and uncheck "Under Top
Bars" if it is checked.
In ios7, there appears to be a behavior in UIScrollView and all
subclasses whereby the content inset and offset is automatically adjusted, sometimes not very well. You
can try disabling that either in code or IB (see link for
how: iOS 7 -- navigationController is setting the contentInset and ContentOffset of my UIScrollView)
For any newly created iOS >7.0 app I suggest you take a deeper look at autolayout. For all my old iOS 6 apps I solved it like this:
In your UITableViewController interface:
bool _hasStatusBar;
bool _hasStatusBarHeight;
UIView *_blendView;
In your UITableViewController implementation file:
-(void)viewWillAppear:(BOOL)animated{
_hasStatusBar = NO;
_blendView = nil;
[self.tableView reloadData];
}
-(void)viewWillDisappear:(BOOL)animated{
_hasStatusBar = NO;
_blendView = nil;
}
- (void) viewDidLayoutSubviews {
// WTF APPLE!?
if (!_hasStatusBar) {
int topBarOffset = 20;
_hasStatusBar = YES;
// Fix for iOS 7 overlaying status bar
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect viewBounds = self.view.bounds;
_blendView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, viewBounds.size.width, topBarOffset)];
[_blendView setBackgroundColor:COLOR_MAIN];
[_blendView setOpaque:YES];
[_blendView setAlpha:1.00];
UIView *whityMacWhite = [[UIView alloc] initWithFrame:CGRectMake(0, 0, viewBounds.size.width, topBarOffset)];
[whityMacWhite setBackgroundColor:[UIColor whiteColor]];
[whityMacWhite setOpaque:NO];
[whityMacWhite setAlpha:0.80];
[_blendView addSubview:whityMacWhite];
[self.view.superview addSubview:_blendView];
}
}
}

Custom animation between UIViewControllers using transitionFromViewController:toViewController:duration

I am trying to use UIViewController's transitionFromViewController:toViewController:duration method but with a custom animation.
I have the following two view controllers added as children to a custom container UIViewController:
firstController - This is an instance of UITabBarController
secondController - This is a subclass of UIViewController
The following code works as expected:
[self transitionFromViewController:firstController
toViewController:secondController
duration:2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^(void){}
completion:^(BOOL finished){}];
However I would like to create a custom animation where the where firstController slides to the left and is replaced by secondController sliding in from the right similar to how UINavigationControllers push and pop methods work. After changing the options to UIViewAnimationOptionTransitionNone I have tried to implement custom animations in the animations block but have had absolutely no success. firstController is immediately swapped for secondController without and animations.
I would really appreciate any help.
Thank you
This is actually really easy. For some reason I assumed that the secondController's view would be be under/ behind that of firstController's. I had only tried to animate the firstController's view. This of course is wrong. As soon as transitionFromViewController:toViewController:duration is called secondController's view is placed over firstController's view. The following code works:
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
secondController.view.frame = CGRectMake(width, 0, width, height);
[self transitionFromViewController:firstController
toViewController:secondController
duration:0.4
options:UIViewAnimationOptionTransitionNone
animations:^(void) {
firstController.view.frame = CGRectMake(0 - width, 0, width, height);
secondController.view.frame = CGRectMake(0, 0, width, height);
}
completion:^(BOOL finished){
[secondController didMoveToParentViewController:self];
}
];

UIScrollView, paging and rotation: Second view is not aligned properly after rotation

I am using a UIScrollView with Paging enabled and the following code to add subviews (core plot charts) to it.
The horizontal scrolling between the views works properly.
However, when showing the second view and then rotating from landscape to portrait mode, the second view is shifted partly to the right and a portion of the first view's right hand side is shown on the left side, hence "destroying" the paging mode.
Could you help me with these issue please? I tried many alternatives, but can't find my bug. Thank you so much!
This is how my iPad screen looks after rotating to portrait mode with the second view:
:
This is my viewDidLoad method:
- (void)viewDidLoad {
self.scrollView.contentSize = CGSizeMake(768 * 2, 400);
chart1 = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(0, 0, 768, 400)];
chart2 = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(768, 0, 768, 400)];
self.scrollView.autoresizesSubviews = NO;
chart1.autoresizesSubviews = YES;
chart2.autoresizesSubviews = YES;
self.scrollView.contentOffset = CGPointZero;
self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
chart1.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
chart2.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth ;
[self.scrollView addSubview:chart1];
[self.scrollView addSubview:chart2];
}
This is how I have implemented rotation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (UIDeviceOrientationIsPortrait(fromInterfaceOrientation)) {
self.scrollView.contentSize = CGSizeMake(704 * 2, 400);
chart1.frame = CGRectMake(0, 0, 704, 400);
chart2.frame = CGRectMake(704, 0, 704, 400);
}
else {
self.scrollView.contentSize = CGSizeMake(768 * 2, 400);
chart1.frame = CGRectMake(0, 0, 768, 400);
chart2.frame = CGRectMake(768, 0, 768, 400);
}
}
I think you should change the contentOffset of the scrollview when rotation is taking place.
You should have a way to know which page is currently displayed before rotation (maybe put this information in a variable). Then in your didRotate.. method set the contentOffset of the scrollview after resizing it, like this:
CGFloat offset = self.scrollView.frame.size.width * currentPageIndex;
[self.scrollView setContentOffset: offset];
As an alternative to laying out your subviews in your view controller, have you considered subclassing your UIScrollView and overriding it's layoutSubviews method? You might also consider defining your dimensions as percentages rather than fixed points - because the point values will shift according to rotation and presence of other UI elements such as navigation and toolbars. You may run into trouble as you're manually resizing UI elements in your rotation method, at the same time that the UI is going to be attempting to automatically resize elements according to your resizing masks. Just my thought...

UIScrollView, Pull down effect similar to CalcBot

I'm trying to make a UI similar to CalcBot where you can pull down the view controller with your finger and it reveals a hidden background view.
I think this is done with the UIScrollView but cannot work out the exact method to have for example 2x Horizontal views with one of them having a vertical.
I do have the horizontal scroll working fine, i'm just stuck with how I can make screen 1 pull down with a vertical scroller.
Any tips or pointers would be appreciated.
Thanks Aaron
You can have nested scrollviews to get scrolling working in both directions. Let's have a vertical scroller inside a horizontal scroller (though you can very well do it the other way round too), so views 3 and 1 get into a vertical scrollview, which in turn gets inside a horizontal scroller along with view 2.
You could create such a view hierarchy in code (probably in the main viewcontroller's viewDidLoad). The following snippet assumes view1, view2 and view3 as webviews.
CGRect bounds = self.view.bounds;
// main guy is a horizontal scroller
UIScrollView *hScrollView = [[UIScrollView alloc] initWithFrame:bounds];
hScrollView.contentSize = CGSizeMake(bounds.size.width * 2, bounds.size.height);
hScrollView.delegate = self;
[self.view addSubview:hScrollView];
// the horizontal scroller contains a vertical scroller
UIScrollView *vScrollView = [[UIScrollView alloc] initWithFrame:bounds];
vScrollView.contentSize = CGSizeMake(bounds.size.width, bounds.size.height * 2);
vScrollView.delegate = self;
[hScrollView addSubview:vScrollView];
// add view3 and view1 to the vertical scroller
UIWebView *view3 = [[UIWebView alloc] initWithFrame:bounds];
[view3 loadHTMLString:#"<h1>3</h1>" baseURL:nil];
[vScrollView addSubview:view3];
UIWebView *view1 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, 0, bounds.size.height)];
[view1 loadHTMLString:#"<h1>1</h1>" baseURL:nil];
[vScrollView addSubview:view1];
vScrollView.contentOffset = CGPointMake(0, bounds.size.height);
// add view2 to the horizontal scroller
UIWebView *view2 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, bounds.size.width, 0)];
[view2 loadHTMLString:#"<h1>2</h1>" baseURL:nil];
[hScrollView addSubview:view2];
// enable paging in both directions
hScrollView.pagingEnabled = TRUE;
vScrollView.pagingEnabled = TRUE;
That would have you scrolling in both directions but that would also let you scroll-right from view3 to view2. If you want to prevent that, you should disable scrolling on hScrollView whenever the vScollView is showing view3. You can do that in scrollViewDidEndDecelerating: in the delegate of vScrollView. Something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.verticalScrollView) {
self.horizontalScrollView.scrollEnabled = (self.verticalScrollView.contentOffset.y > self.view.bounds.origin.y);
}
}

NSScrollView doesn't display vertical scrollbar after adding content

I've created an NSScrollView which itself contains a NSClippedView as content view (this is all default, created by IB). Inside the contents view there is the (default) document view.
This NSScrollView has horizontal scroller disabled and vertical enabled and, most importantly auto hide scrollers enabled.
When I add new views (via code, runtime) to the document view the scroller does not unhide automatically, until the moment I vertically resize the window (and which in turn resizes the scrollview as well). 1px is enough. Just the new painting of the window seems enough.
What I am looking for is triggering this by code: so when I add views to the scrollviews' content view I would like the scrollbar to appear.
int numberOfChildViews = 10; //hard coded for example here
int childViewHeight = 80; //same as above
NSRect rect = NSMakeRect(0, 0, [[self.scrollView contentView] bounds].size.width, [numberOfChildViews*childViewHeight);
[[self.scrollView documentView] setFrame:rect];
[[self.scrollView documentView] setBounds:rect]; //just added to make sure
Then I added the custom views to the document view, like:
for (int i=0; i<numberOfChildViews; i++) {
NZBProgressViewController *item = [nzbProgressArray objectAtIndex:i];
int y=i*[[item view] bounds].size.height;
rect= NSMakeRect(0, y, [[scrollView contentView] frame].size.width, [[item view] bounds].size.height);
[[item view] setFrame:rect];
currentPosition++;
}
I am using a FlippedView so the origin will be displayed in left-top, like so:
#interface NSFlippedClipView : NSClipView {
}
#implementation NSFlippedClipView
- (BOOL)isFlipped {
return YES;
}
- (BOOL)isOpaque {
return YES;
}
#end
And added the following code to the awakeFromNib
NSFlippedClipView *documentView = [[NSFlippedClipView alloc] init];
[documentView setAutoresizingMask:NSViewWidthSizable];
[documentView setBackgroundColor:[self.scrollView backgroundColor]];
[self.scrollView setDocumentView:documentView];
[documentView release];
The scrollbars should become visible as soon as the document view is resized to be larger than the current viewport of the scroll view. Are you resizing the document view when you add your subviews to it?
Ahh, it's my own bad. For future reference: if you want to move the origin (0,0) to left-top use a NSView instead of NSClippedView extended class with IsFlipped method overriden to YES.
Thanks irsk for answering.