iOS8 - animate all subviews not working - ios

I'm trying to animate all views up when showing the keyboard to avoid overlap.
However, this old trick no longer seems to work:
for (UIView *subview in self.view.subviews){
CGRect r = subview.frame;
r.origin.y += yOffSet;
[UIView animateWithDuration:duration animations:^{subview.frame = r;} completion:block];
}
Any idea why?
==== UPDATE
These subviews don't animate on my iPhone6 running iOS8. They do however on my iPhone4 running iOS 7.1.2. Both use AutoLayout.

If you are using Auto-Layout you should not animate using frames anymore but constraint constants.
Also keep in mind that the "subviews" array property only contains the immediate "children" views. This could affect things, depending on your setup.
Edit I just noticed the problem only occurs on iOS7/iphone 4. There is a known issue with Auto-Layout and iOS7/iOS8 compatibility.
It comes down to how the updates are propagated along the view hierarchy.
There is a number of stackoverflow posts on this subject:
Auto-Layout Issues: iOS 7 vs iOS8
Issue with Auto Layout on iOS 8 (code works perfectly on iOS 7)

Related

Auto-Layout Issues: iOS 7 vs iOS8

I am trying to create a collapsible toolbar that works like this (running in iOS 7 -- ugly colors etc. for visualization purposes):
However, when I run the code in iOS 8, this is what happens:
I have set up a constraint system that is based on the following:
A centering view (not shown) keeps the toolbar in the screen middle.
A sizing view is adjusted to collapse the toolbar. The sizing view is anchored to the right of the centering view(via a trailing constraint).
A container view holds the actual content of the toolbar. It is anchored to the right of the sizing view (also via a trailing constraint).
Various content views are contained in the container view. They have no constraints. The default constraints applied by the system should be width, height, top, left, which ensures that they keep their relative positions in the container view.
The collapsing of the toolbar is achieved as follows:
- (IBAction)showLess:(id)sender {
self.widthConstraint.constant = 50; // adjust this number for collapse / expand
[UIView animateWithDuration:0.3 animations:^{
[self.centeringView layoutIfNeeded]; // trigger animation
}];
}
Which adjusts the width of the sizing view.
Problem:
iOS 8 seems to behave as if I had left anchored the content view, but this is not true.
I would sincerely appreciate:
An explanation as to why iOS 8 would have such a radically different interpretation of the given (reasonably simple) constraints.
A pointer as to how I can get the intended behavior in iOS 8
Source code available here (updated version that works in iOS 8).
UPDATE:
The issue was solved with answers from Stack-overflow. Basically, the right answer is this, but it was nicely summarized in this answer.
The difference between iOS7 and iOS8 is not in the way the constraints are interpreted, but in the way that update commands are trickled down through the view hierarchy.
When I implemented the behavior first in iOS 7, I noticed that the animation would only work properly if I called layoutIfNeeded on the parent view of the sizing view (i.e. on centering view). In iOS 7 this apparently trickled down the view hierarchy automatically. In iOS 8, this is not the case. You have to manually invalidate the view whose constraints have changed with setNeedsLayout, and then update the layout with layoutIfNeeded. My solution in the updated code looks like this:
- (IBAction)showLess:(id)sender {
self.widthConstraint.constant = 50;
[self.sizingView setNeedsLayout]; // *** THIS LINE IS NECESSARY TO MAKE THINGS WORK IN iOS 8
[UIView animateWithDuration:0.3 animations:^{
[self.sizingView layoutIfNeeded]; // trigger animation
}];
}
I hope this helps others who are also stuck on this forward compatibility issue.
The issue was solved with answers from Stack-overflow. Basically, the right answer is this, but it was nicely summarized in this answer. The difference between iOS7 and iOS8 is not in the way the constraints are interpreted, but in the way that update commands are trickled down through the view hierarchy. When I implemented the behavior first in iOS 7, I noticed that the animation would only work properly if I called layoutIfNeeded on the parent view of the sizing view (i.e. on centering view). In iOS 7 this apparently trickled down the view hierarchy automatically. In iOS 8, this is not the case: You have to manually invalidate the view whose constraints have changed with setNeedsLayout, and then update the layout with layoutIfNeeded. My solution in the updated code looks like this:
- (IBAction)showLess:(id)sender {
self.widthConstraint.constant = 50;
[self.sizingView setNeedsLayout]; // *** THIS LINE IS NECESSARY TO MAKE THINGS WORK IN iOS 8
[UIView animateWithDuration:0.3 animations:^{
[self.sizingView layoutIfNeeded]; // trigger animation
}];
}
I've updated the question to include the answer, but in response to #unmircea I am posting a separate answer, as well.

Setting "edgesForExtendedLayout = UIRectEdgeNone" does not prevent my subview from moving up in iOS 7

I have a view controller that was written back in the days of iOS 5 and I'm trying to transition it to iOS 7. After reading the iOS 7 transition guide and poking around on SO, I discovered that I need to set the new iOS 7 property edgesForExtendedLayout to UIRectEdgeNone to prevent one of my custom subviews from appearing 49 pixels higher on iOS 7 than it appears on iOS 6. However, after setting that property, my custom subview is still appearing 49 pixels higher on iOS 7 and I don't know what else I need to do. Here's my simple code that I added to my viewDidLoad method...
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
and here's the code for creating and adding the custom subview that is appearing higher on iOS 7...
CGRect customControlFrameRect = {{0.0f, 240.0f}, {100.0f, 100.0f}};
self.customControl = [[MyCustomControl alloc] initWithFrame:customControlFrameRect];
self.customControl.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:self.customControl];
One other important detail, if it helps, is this view is created from a nib file, but the custom subview that is appearing higher on iOS 7 than iOS 6 is the only subview that is created and added programmatically in viewDidLoad, after I set the edgesForExtendedLayout property. All the other subviews that are created from the nib aren't affected regardless of the whether or not I set the egdesForExtendedLayout property.
My two questions are...
Why is my custom subview appearing higher on iOS 7 even after I set the edgesForExtendedLayout property to UIRectEdgeNone?
Why aren't the other subviews (the subviews that are loaded from the nib) appearing higher in iOS 7?
Thanks in advance for your wisdom!
1.
It is the purpose of edgesForExtendedLayout which was set to UIRectEdgeNone to not extend the view in any direction, but your view will now start in origin within the screen size (now below the statusBar!) when using a frame with position of 0, 0 (like you did with the customControl). You need to adjust the frame of your view/viewController to represent that you want to start under the statusBar/navigationBar or make the status/nagivationBar opaque; or add UIRectEdgeTop if only used for one view.
Setting the navigationController to opaque will also do so with the statusBar:
self.navigationController.navigationBar.translucent = NO;
2.
Because the interface builder sets the frame correctly under the navigationBar by default, you could change it of course. And it uses UIRectEdgeAll and autoresizing.
I've found, in some cases, that setting the edgesForExtendedLayout in viewWillAppear: rather than viewDidLoad gives me the result I'm looking for. This may not solve your problem, but it has helped me in a few cases and hopefully it'll help others.
if( [self respondsToSelector:#selector(setEdgesForExtendedLayout:)] )
{
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
}

Having issues applying solutions for iOS 7 status bar overlap

I've got the issue where my iOS6 app's UI overlaps with the status bar on iOS 7. I've read previous posting on here regarding how to fix this but I'm not getting results.
I have several views all contained within a view controller's main view:
Posted solutions to the status bar overlap issue suggest to add 20 to the Y origin and set a value of -20 to the delta Y to compensate.
The first issue is I can't set the origin in IB for my main high level view - its greyed out:
So instead in the VC's viewDidLoad a tried setting the origin manually:
- (void)viewDidLoad
{
CGRect frame = self.view.frame;
frame.origin.y = 20;
self.view.frame = frame;
However when I run the app there's no difference, I'm still getting the status bar overlapping.
I'm only making changes to the main view, as all the other views are children of the main view I was assuming if I adjust just that one everything else will shift relative to that.
How come I'm not seeing any difference after making a change to the origin?
try this combined with your proposed solution in viewDidLoad:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
Another thing you can try is setting the frame on viewdidappear if this does not work for you.
Good luck and upvote up if this works for you :)

ios7 all searchviews and tableviews off by 20 pixels

EDIT: This is not an issue with the frame. The frame is in the correct spot. It is the content that is off. Yes, we are compensating for clear status bar. ViewDidLoad shows content offset is at 0,0.
On every page of my applcation that has a scrollview it's content is pushed down by 20 pixels. I think it may have something to do with the new StatusBar, or lack thereof. The bug also seems to be present in the simulators settings menu. Screenshot below. This only seems to affect the first scroll view that is added to a ViewController and only if no other views have been added to it. Table views are also being affected because they inherit from Scroll view. To be clear the scrollview starts at the correct origin but the content is pushed down by 20 pixels. This is bizarre because I would think if it was a status bar issue it would be 20 pixels above not below.
Currently we are fixing it by adding this to our base view controller
UIView *hackView = [[UIView alloc] init];
hackView.frame = CGRectMake(0, 0, 0, 0);
[self.view addSubview:hackView];
Obviously, this is a hack.
The same thing is happening on phone and in simulator even with the Gold Member version of IOS7 when building for latest IOS7. You can even see an example of the bug in the simulator's settings tableview and also in ours below.
This is a screenshot immediately after it loads. One bizarre thing that we noticed is that when we execute a pull to refresh it will correct the scrollview and rest at it's correct location. Alternatively, if we compensate it will look correct in the beginning but any subsequent pull to refreshes will migrate the scrollview to -20px
After further testing in Xcode, my original workaround still works, but the real culprit seems to be a Navigation Controller combined with the Adjust Scroll View Insets flag on the view controller. Disabling that solved the issue.
[self setAutomaticallyAdjustsScrollViewInsets:NO];
Original workaround: Try reordering your view hierarchy or add an empty view at the top.
This happened to me, and it appears that when a UIScrollView or similar subclass is first in the hierarchy it gets offset by 20 pixels (to keep it from hitting the status bar). However, this still occurs even when the view in question is nowhere near the top.
This is reproducible in Interface Builder, and an easy workaround for me has been to reorder my views so that a label or button is first under the view controller's view. If that's not possible in your case, adding an empty view (even off screen) above your scrollview also seems to solve the problem.
I added this to my UIViewControllers in viewDidLoad: which were affected and it fixed the issue for me:
NSComparisonResult order = [[UIDevice currentDevice].systemVersion compare: #"7.0" options: NSNumericSearch];
if (order == NSOrderedSame || order == NSOrderedDescending)
{
// OS version >= 7.0
self.edgesForExtendedLayout = UIRectEdgeNone;
}
Another, cleaner method:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
How are you laying out your views? With autolayout you can use the topLayoutGuide, which should accommodate for these offsets.
In the Ios 7 the view starts from the window from y= 0, move your view to 20 pixel dowm
self.view.frame = CGRectmake (0,20,320,hight);
this is due to transparent status bar in iOS 7.
pull down the view to show it properly.

UIScrollView layoutSubviews behavior changes in iOS 5?

I'm working on a component that is a subclass from UIView and contains a UIScrollView.
When scrolling, I noticed different behaviors depending on which SDK I build with. On iOS 4 the layoutSubviews message is send on the scroll view's superview (which is my component) but on iOS 5 it seems that the message is not send anymore...
After taking a look at the iOS 5 release notes and changelog, I did not find any mention of such a change. Did I miss somethin?
In iOS5, layoutSubviews is not called on a scrollView's superview. But it was in iOS4.
If you want this behavior in iOS5, do this in your subclass of UIScrollView:
- (void)layoutSubviews {
[super layoutSubviews];
// causes layoutSubviews to get called on superview
[self.superview setNeedsLayout];
This was probably changed to be more efficient. Just because UIScrollView is scrolling, doesn't mean it's superview needs to layout itself.
I had big probs with resizing the size of button witch was subview in tableview. The nib loaded the smaller button and after loading I resize it. But the table view content didn't. (In iOS 4.* it was perfect but in iOS 5). So I figured out that I have to place my resizing in ViewDidLoad. I hope it helps to some1 =)

Resources