Keeping UIScrollView Position after Interface Orientation Change - ios

I have a UIScrollView that updates its frame after each interface orientation change, keeping its height but dynamically updating its width to always fill the new width. Since it is a horizontal scroll view with paging enabled, naturally, I am updating its content according to its new size. I do this as follows (obviously it is a simplified code, but you should get the idea):
scrollView.frame = CGRectMake(0.0f, 0.0f, window.size.width, 200.0f);
// Content is being deleted here
// Then redraw with the new width in mind
scrollView.contentSize = (window.size.width * [database count]);
[scrollView setContentOffset:CGPointMake(currentPostionX, 0.0f) animated:NO];
This method works just fine; the scrollview keeps its previous position after each interface orientation change. My problem is that it is not smooth. In fact it is quite ugly, often resulting in a jittery, jumping "animation" when the contentOffset is being reset (and it doesn't matter if I set 'animated' to YES).
There is got to be a better way of doing this and make the scroll view keep its position according to its new dimensions.
Any ideas?
Update: Actually, I call the above methods in the viewWillTransitionToSize's animateAlongsideTransition section, as this creates quite a nice transition while changing between different sizes. If I put them into the completion section, everything just "pops" into place after the rotation, which is quite ugly. This way everything is smooth, except the jumping at the right position at the very end.

In this answer, I will assume that your statement:
...UIScrollView that updates its frame after each interface orientation change...
truly means after each interface orientation change.
If so, use -viewWillTransitionToSize:withTransitionCoordinator: and visit the documentation here.

Related

Autolayout: UIView frame wrong after rotation

I have created my own implementation of a UITabBar (just a UIView instance).
This UIView contains 3 TabBarItem instances. A TabBarItem is a UIView subclass, and each contains the following UI controls:
UIImageView
UILabel
TabBarBadge (custom UIView subclass)
I am laying out the view hierarchy in a storyboard using auto layout.
The badge for a tab bar item should be positioned so that the y-center of the badge is aligned with the top edge of the image view, and it should be aligned to the right side of the image view, with a 4 point overlap (so, left edge of badge aligned with right edge of image view, with a constant of -4).
This works fine when the app loads, whether in portrait or landscape mode. In either case, though, rotating the device ends up with the frame of the badge in the wrong place after rotation finishes.
Here's a printout from the console of the NSLayoutConstraint in question:
<NSLayoutConstraint H:[UIImageView]-(-4)-[TabBarBadge] (active)>
That's exactly what I would expect it to be.
Here's a printout from the console of the TabBarBadge in question (portrait mode; correct presentation):
<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>
Here's a printout from the console of the TabBarBadge in question (landscape mode; incorrect presentation):
<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>
So, you can see that the frame isn't changing/updating when the rotation occurs.
Here are a couple of screen shots:
Portrait, Correct Display
Landscape, Incorrect Display
Constraints (Printout of constraints above is from this view)
So, what has me confused is that the rest of the TabBarItem is updating correctly. The UIImageView and UILabel are automatically updating correctly. Why is this one frame not updating correctly?
So, first I researched whether a custom drawRect: method might be the cause of my problem. That doesn't appear to be a factor.
Then, I started noticing some odd discrepancies between the view's frame at different times, and how only this particular one (there are actually 3 in the app) was having the problem. The other 2 currently have no values displayed.
Then, I remembered that I use a UIDynamicAnimator when the value is changed to "bounce" the view, in order to bring attention to the new value.
So, the problem is actually that I wasn't "releasing" (in the control sense, not the memory sense) the view from the UIDynamicAnimator when I was done with the animation. (I'm still not sure whether that's a bug or not. It seems to me that if the animation is paused and the constraints should update the view's frame, then perhaps that should occur.)
So, here's the actual code that was "buggy":
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
// {Other code}
}
And here's the code that fixed my issue:
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
// {Other code}
// Code that fixed the issue:
[self.animator removeAllBehaviors];
}
If I need to animate the view again, I just re-create the animator anyway, so this seems to be the best fix.
This appears to be in agreement with Matt's answer here. (I didn't know this was an issue with UIDynamicAnimator when I asked the question.)

layoutIfNeeded not working in iPad

layoutIfNeeded is not working properly in iPad but it is working on iPhone. I want to a circular button. On the iPhone the circle is generated properly but in iPad it looks like a rhombus shape.
_btnthur.layer.cornerRadius = _btnthur.frame.size.width/2;
[self._btnthur layoutIfNeeded];
Expected Result :
Current Output:
I can see two main issues here:
In your code here you are setting the cornerRadius on btnthur, but telling btnSat to layout.
You should not be calling layoutIfNeeded, you should be calling setNeedsLayout and allow the run cycle to coalesce layout calls for performance.
Hope this helps :)
Edit for updated question:
Your corner radius is wrong in the second one, it is much too large. Can you check that the frame is correctly set on the button at the point you are setting the corner radius?
You may want to move your code
_btnthur.layer.cornerRadius = _btnthur.frame.size.width/2;
[self._btnthur setNeedsLayout];
into layoutSubviews or viewDidLayoutSubviews (depending on view or view controller) and make sure that the corner radius is always updated when the frame (or more importantly, the size) is set.

UIScrollView includes extra space at bottom

I have a UIScrollView which continually adds extra space at the bottom of its scroll area on iOS 7. If I rotate the device, the problem fixes itself, however, before rotating after first navigating to the view controller, the extra space always appears. I've included an image below:
What I've tried:
Set automaticallyAdjustScrollViewInsets to NO
Checked contentSize, contentOffset and contentInset. All appear fine.
I'm not sure what else to try to solve this issue, and would greatly appreciate any help or direction here!
Edit: As another layer of complexity to this issue... I call a method, updateScrollViewContentSize in my viewDidAppear: method. When I rotate, and it somehow fixes itself, this same method is called. So my contentSize is being set when my view appears and when I rotate.
To make things worse, the content size is always set to 638... Whether there is extra space or not. But, for some reason, the scrollView ignores this until we rotate.
Here is the method to update the contentSize:
- (void)updateScrollViewContentSize {
[self.scrollView setContentSize:CGSizeMake(self.scrollView.frame.size.width, (self.feedbackFooterView.frame.origin.y + self.feedbackFooterView.frame.size.height + 20))];
}

UIScrollView not working

I have a UIScrollView with a View inside of it. Inside of that view is a bunch of buttons, labels, etc that fit in the View when in Portrait mode...When the iPad is rotated, I want the scrollView to kick in so the user can scroll to the bottom of the view. The app runs, but when I rotate it, the scroller never works...I believe I've wired everything up correctly and I have this code in the viewDidLoad event:
[scrollview addSubview: masterView];
scrollView.contentSize = [masterView sizeThatFits:CGSizeZero];
Is there something else I am missing? Do I need to modify the size when the iPad rotates?
thanks
There may be a problem with the content size of the UIScrollView. Without the contentSize being set larger than the actual scrollView size, scroll bars won't be shown.
You can code this in with something like this:
[scrollView setContentSize:CGSizeMake(2000,2000)];
And then changing the content size to the actual content size of what you are putting in the UIScrollView (scrollView).
If you look at the results of [masterView sizeThatFits:CGSizeZero] (e.g. NSLog or set a breakpoint in your debugger, I think you will find that it's not what you expected it to be. You might find that masterView has autoResize parameters set (which is common for a view that covers the entire screen), which means that it might, itself, be getting resized too short to fit all of its controls and scrollView is simply grabbing this shortened value itself. Take a look at that CGSize and the problem will be obvious.
I faced similar situation but my case was iPhone.
Remember that content should be larger than scroll for scrollView to kick in.
"Why would you want to go down if everything is visible in front of you ?"
use the following code:
[scrollView setContentSize:CGSizeMake(intValue, intValue)];
intValue: Integer values setting width and height of scroll.
Even if it doesn't works, don't worry there are loads of other options to figure out the solution:
1. NSLog
2. Breakpoints
3. Put up errors you are getting from console on stackoverflow

UIView coordinate transforms on rotation during keyboard appearance

iPad app; I'm trying to resize my view when the keyboard appears. It amounts to calling this code at appropriate times:
CGRect adjustedFrame = self.frame;
adjustedFrame.size.height -= keyboardFrame.size.height;
[self setFrame:adjustedFrame];
Using this technique for a view contained in a uisplitview-based app works in all 4 orientations, but I've since discovered that a vanilla uiview-based app does not work.
What happens is that apparently the uisplitview is smart enough to convert the coordinates of its subviews (their frame) such that the origin is in the "viewer's top left" regardless of the orientation. However, a uiview is not able to correctly report these coordinates. Though the origin is reported as (0,0) in all orientations, the view's effective origin is always as if the ipad were upright.
What is weird about this is that the view correctly rotates and draws, but it always originates in the literal device top left. How can I get the view to correctly make its origin the "top left" to the viewer, not the device's fixed top left? What am I missing? Please, for something so trivial I've spent about 6 hours on this already with every brute force technique and research angle I could think of.
This is the original source which doesn't work in this case:
move up UIToolbar
OK, I don't know what the ACTUAL answer is to the original question, but I can say with certainty that one way to resolve the issue is to always ensure that you don't manipulate a viewController's view directly. Always wrap your view inside a container view inside the main "view", then have that container view adjust its position etc as needed. Works exactly as the splitview does, probably because in both cases now the view in question is a subview of the main "view". What a relief!

Resources