UIScrollView + change orientation = messed up subviews (iPad) - ios

I want to use a UIScrollView as my main container in the app, enabling me to swipe back and forth between subviews. To achieve this, I created a UIViewController subclass with a UIScrollView IBOutlet:
In the viewDidLoad method I construct the sub-pages:
for (int i= 0; i< pageCount; i++)
{
CGRect frame = self.scrollView.frame;
frame.origin.x = frame.size.width * i;
frame.origin.y = 0;
UIWebView* aWebView= [[UIWebView alloc] initWithFrame:frame];
[self.scrollView addSubview:aWebView];
}
When launching the app (portrait mode), everything works. That is, the UIWebViews are layed out side by side with the correct dimensions, and I can swipe back and forth between them.
When I rotate to landscape, it seems that neither the scrollview size nor the subviews are resized.
I don't know what I should do in order to resize the subviews and the scrollview itself, or at what point in code I should do anything, and I cant seem to find any examples for this.
Anyone know what to do?
[edit] Attempt to adjust sizes as suggested by mahboudz:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * pageCount, self.scrollView.frame.size.height);
for (int i= 0; i< pageCount; i++)
{
CGRect frame = self.scrollView.frame;
frame.origin.x = frame.size.width * i;
frame.origin.y = 0;
UIWebView* view= [[self.scrollView subviews] objectAtIndex:i];
view.frame= frame;
}
}
This kind of does what I want, but has the following issues:
1) one can see the subviews grow to correct screen size upon changing orientation
2) when the current page is, for example, page 2 of 5 pages, the page is not fully visible after orientation was changed, but is off-screen by like 40 pixels
3) I get strange effects depending on whether the app is launched in portrait or landscape mode (simulator), ill try to explain:
When the app is launched in portrait mode:
The shape/border of the subviews looks messed up/offscreen, see screenshots:
http://i53.tinypic.com/21jr76x.png
when I rotate to landscape, everything looks okay, scrolling works superb. even when I rotate back to portrait, everything is great now:
http://i55.tinypic.com/if3iiw.png
When the app is launchend in landscape mode:
I get the same messed up/offscreen glitches as in portrait mode
Switching back and forth between portrait and landscape fixes this for landscape mode
BUT: Portrait mode will have the subviews with the width of the landscape mode, thus subviews are too wide
I tried to fix 1) doing the code above in willRotateToInterfaceOrientation however it completely messed up the layout.
I fixed 2) by adding the following code to didRotateFromInterfaceOrientation:
// update the scroll view to the appropriate page
CGRect frame = self.scrollView.frame;
frame.origin.x = frame.size.width * self.currentPage;
frame.origin.y = 0;
[self.scrollView scrollRectToVisible:frame animated:NO];
Note: current page is determined in scrollViewDidScroll
I dont have any idea how to fix 3)

You would need to reset the frame size, content size and the content offset in order to get the subviews in a proper position.
CGFloat screenHeight =[UIScreen mainScreen].bounds.size.height;
CGFloat screenWidth =[UIScreen mainScreen].bounds.size.width;
self.scrollView.frame = CGRectMake(0, 0, screenWidth, screenHeight);
self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * numberOfPages, self.scrollView.frame.size.height);
self.scrollView.contentOffset = CGPointMake(visiblePageBeforeRotation * self.scrollView.bounds.size.width, 0);
This code should be placed in the method
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
Check as well the answer on this question:
Clean autorotation transitions in a paging UIScrollView
It has good example named Rotolling for rotating UIScrollView with paging enabled.
Hope this helps.
P.S: I am facing a problem on repositioning the center of the UIWebView on the rotation.

You need to implement viewWillRotate/viewDidRotate and make adjustments to our content size and orientation as needed.

Related

Enable stretch layout option to support bigger iPhones

Our current version of our iphone application in the app store simply stretches the layout to fill the iphone 6 and 6+. After the xcode 7 update, the app just runs as it would on an iphone 4 with black bars at the top. I understand that adding a launch storyboard resolves this. The problem then is that our layout is out of whack on the 6 and 6+.
Obviously fixing the layout to adapt to these screen sizes is what needs to be done, but we are in a pinch and just need to get a behind the scenes function update pushed out to our users ASAP. Is there anyway to go back to the old behavior where the UIView would just stretch to fill the screens?
This won't be an immediate fix, but it's a quick patch that might be worth trying.
If your view is always in portrait, this should work for you. If it requires rotating or a UISplitViewController on iPhone 6(s)+, then it might not.
Scale each subview in your view controller for the new screen size. Declare a couple macros for the device screen size:
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
Then add a method to your view controllers that goes through all subviews to resize each view proportionately based on an iPhone 4 screen size.
- (void)resizeSubviews:(UIView *)view {
CGRect frame = view.frame;
frame.origin.x = frame.origin.x / 320.0f * kScreenWidth;
frame.origin.y = frame.origin.y / 480.0f * kScreenHeight;
frame.size.width = frame.size.width / 320.0f * kScreenWidth;
frame.size.height = frame.size.height / 480.0f * kScreenHeight;
view.frame = frame;
for (UIView *subview in view.subviews) {
[self resizeSubviews:subview];
}
}
Then call that method after adding your subviews with:
[self resizeSubviews:self.view];
There are a lot of ways this could not work, but it covered me during the switch from iPhone 4s to iPhone 5.

Deciding height for UIScrollView with UIWebView inside

I have a detail view where I want to show a title, subtitle and content for articles. I want to be able to use HTML to format the text, so I've used a UIWebView for showing the article body. This works perfectly.
How ever, all of this, is inside a UIScrollView, so my issue is that I have to calculate the height of the UIScrollView?
This is how it works today:
And this is how it looks like in Storyboard:
So what I need to find out, is what is the correct code and syntax to calculate the correct height of the UIScrollView? Amongst several things, I tried [self.scrollView sizeToFit] without luck.
EDIT: Apparently it sets the correct heights with the code below, but seems like the view never updates.
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
// get height of content in webview
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"] floatValue];
// set new frame height
CGRect frame = webView.frame;
frame.size.height = height;
webView.frame = frame; // webview is now correct height
// set new frame height for scrollview (parent of webview)
CGRect scrollFrame = self.scrollView.frame;
scrollFrame.size.height = webView.frame.origin.y + height;
self.scrollView.frame = scrollFrame;
// log to console for cross checking
NSLog(#"new frame: %f, scrollview frame: %f", scrollFrame.size.height, self.scrollView.frame.size.height);
}
The console reports the apparently correct height:
new frame: 582.000000, scrollview frame: 582.000000
And a quick check in Photoshop as well, this seems to be correct:
The summed value of green and blue area is 582 pixels, but the scrollview still just scrolls the 504 pixel area from below the navigation bar to the bottom of the screen (to the bottom of the tab bar).
The webview has internally a scrollview. You can query its size by webview.scrollView.contentSize. You have to wait with this until the webview has finished rendering.
So, in the -webViewDidFinishLoad: delegate method you can get the optimal height of the webView through webView.scrollView.contentSize.height. You can then resize the webView to this height and layout the other views appropriately. If all of this is done in a custom view, the proper way of doing this would probably be to just call [theView setNeedsLayout] and override -layoutSubviews in theView.
You also should set webView.scrollView.alwaysBounceVertically to NO.
I solved the problem.
First of all, just expand the UIWebView to a height higher than the content ever will be (e.g. 2000 pixels).
The delegate method code that makes the magic happen
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
// set height for webiew
webView.autoresizesSubviews = NO;
webView.translatesAutoresizingMaskIntoConstraints = YES;
webView.scrollView.autoresizesSubviews = NO;
webView.scrollView.translatesAutoresizingMaskIntoConstraints = YES;
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('content').clientHeight;"] floatValue] + 80; // +80 for tabbar and spacing
CGRect frame = webView.frame;
frame.size.height = height;
webView.frame = frame;
// fix height of scroll view as well
self.scrollView.translatesAutoresizingMaskIntoConstraints = YES;
self.scrollView.contentSize = CGSizeMake(320, (self.webView.frame.origin.y + self.webView.frame.size.height));
}

UIWebView content not adjusted to new frame after rotation

I have an e-mail application,
which shows the content in an UIWebView inside an UISplitViewController.
Everything works fine until i rotate the device, when already zoomed in/out in the UIWebView.
When rotating the device, i adjust the frame of the UIWebView in
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
The problem is, when i zoomed in/out in the UIWebViewand then rotate the device.
The content isn't resized to it's new frame, which leads to a gray/black border in the content, or the content can be scrolled horizontal.
Addional information:
The UIWebViewis a subview on a UIScrollView which can be scrolled.
When the UIWebViewis totally visible the scrollEnabledof the UIScrollViewis disabled and the UIWebView can be scrolled.
Above picture shows the UIWebViewin landscape when selecting an email.
This is the state before rotating in both cases.
[Not zoomed before rotating]Above picture shows that the UIWebView is correctly resizing the content to it's new frame.
[Zoomed before rotating]Above picture shows the UIWebView after zooming in landscape and then rotating to portrait
-> content isn't correctly shown
Information of the UIWebView.scrollView in - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation:
<_UIWebViewScrollView: 0xb1322a0; frame = (0 0; 768 960); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0xb1321d0>; layer = <CALayer: 0xb132270>; contentOffset: {0, 0}>
frame is correct!
Edit: Solution!!
i managed to solve it by updating the viewport width after rotating the device
CGFloat fll_width = self.view.frame.size.width;
NSString* adsl_javascript_string = [NSString stringWithFormat:#"var setViewPortScript = document.createElement('meta');\
setViewPortScript.setAttribute('name', 'viewport');\
setViewPortScript.setAttribute('content', 'width = %f');\
document.getElementsByTagName('head')[0].appendChild(setViewPortScript);", fll_width];
[adsc_webview stringByEvaluatingJavaScriptFromString:adsl_javascript_string];
i managed to solve it by updating the viewport width after rotating the device
CGFloat fll_width = self.view.frame.size.width;
NSString* adsl_javascript_string = [NSString stringWithFormat:#"var setViewPortScript = document.createElement('meta');\
setViewPortScript.setAttribute('name', 'viewport');\
setViewPortScript.setAttribute('content', 'width = %f');\
document.getElementsByTagName('head')[0].appendChild(setViewPortScript);", fll_width];
[adsc_webview stringByEvaluatingJavaScriptFromString:adsl_javascript_string];
This seems to be a bug also in iOS 7 and iOS 8.
A fix would be this:
First resize the frame on layout changes:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_webView.frame = self.view.bounds;
}
Then on the rotation callback reset the zoomScale to 0. Yes, 0!
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// Allow the animation to complete
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_webView.scrollView setZoomScale:0 animated:YES];
});
}
You can also use the new function
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
// Allow the animation to complete
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_webView.scrollView setZoomScale:0 animated:YES];
});
}

Shift frame inside UIPopoverController

My universal app uses NIBs for its settings screens. I'd like to use the same NIBs for both iPhone and iPad.
Thus on iPad, I use a UIPopoverController in the MainViewController and for settings, simply display the iPhone-sized NIBs, to show what is called the SettingsViewController. The popover is sized 320x460 points.
This causes a problem, because the iPhone version draws a number of things above the status bar programmatically, and for the iPad version this is not necessary. Current situation on iPad:
As you can see, there's a big empty space above the "Settings" title. Thus what I want, is to shift the view controller up about 20 points, inside the popover:
The popover is instantiated as follows in the MainViewController:
settingsPopoverController = [[UIPopoverController alloc] initWithContentViewController:popoverNavigationController];
settingsPopoverController.popoverContentSize = CGSizeMake(320, 460);
settingsPopoverController.delegate = self;
popoverNavigationController.navigationBarHidden = YES;
In the SettingsViewController, I set the frame as follows:
- (void)viewDidLoad
{
self.contentSizeForViewInPopover = CGSizeMake(320, 460);
}
And later in the SettingsViewController, I try to create an offset as follows:
- (void)viewWillLayoutSubviews
{
// shift it up
CGRect frame = self.view.frame;
frame.origin.y = -20;
[self.view setFrame:frame];
}
This does not shift the content up a bit. How to go about?
To clarify: I want to move down the "viewport" that the popover shows.
Try to:
myPopover.autoresizingMask=UIViewAutoresizingNone;
Or:
myPopover.autoresizingMask=UIViewAutoresizingFlexibleHeight || UIViewAutoresizingFlexibleWidth;
You can also try to put your code in -(void)viewDidLayoutSubviews method.
If this answers did not help, try set popoverLayoutMargins (property) instead of setFrame: for example:
popover.popoverLayoutMargins=UIEdgeInsetsMake (
CGFloat 50, //top
CGFloat 50,//left
CGFloat 50,//bottom
CGFloat 50//right
);

UIButton does not respond with UIScrollview Paging

I am running into a problem with the uiscrollview. When I have it as a regular scroll view all the UIButtons work as expected. When I add paging to it the buttons no longer respond. I have a top edge sticking out and they do accept the press from there. They are definitely behind the scroll view but they will scroll on and off the page when it is moved like I want them to. I have read a lot of different responses but most will not provide the answer unless you use gesture controllers. I am attaching the piece of code that creates the paging. can you let me know what I might be missing?
-(void)scollPagingSetup:(float)frameSize {
float numberOfPages;
int maxheight;
[self.view setPagingEnabled:YES];
[self.view setBounces:YES];
UIView *first = [[UIView alloc]initWithFrame:self.view.frame];
[self.view addSubview:first];
maxheight = [UIScreen mainScreen].bounds.size.height;
numberOfPages = ceil(frameSize/maxheight);
for (int i = 1; i < numberOfPages; i++) {
CGRect frame;
frame.origin.x = 0;
frame.origin.y = self.view.frame.size.height * i;
frame.size = self.view.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
[self.view addSubview:subview];
}
self.view.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height *numberOfPages);
}
Thank you in advance for taking a look. I found something that would answer this problem but I was unable to grasp what the real solutions was to the problem. here is the link to that question:
IPhone UIButton doesn't respond in a UIScrollView
The problem is that the content size is not set properly so the "clickable area" (content area's size) is also not set... on your last line of code (line before the last "}") you are setting the scrollView's contentSize's frame to itself which will just be 0... try this instead (I use "first" (the nested view) instead of "self" (the scroll-view itself).
self.view.contentSize = CGSizeMake(firstframe.origin.x, (firstframe.origin.y - 20) * numberOfPages);
Note from question-poster on edited answer:
I made a change to the answer. After I worked with the app some more i was not scrolling fully. I made the change to reflect what I did to fix this and cause a full page scroll. the -20 offset was needed due to the offset of the uiscrollview. This offset can be seen in interface builder.

Resources