Impact of calling layoutIfNeeded without calling setNeedsLayout for a view? - uiview

Can we call layoutIfNeeded if we are not calling setNeedsLayout .
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
Is there any benefit of calling layoutIfNeeded, if developer is not calling setNeedsLayout.

Possibly. Changes you make to views that impact auto layout will get picked up at the next drawing cycle. However there might be cases where you want those changes to take effect immediately, say, because you want to use the new position to calculate geometry for other views. In those cases, call layoutIfNeeded immediately after your changes.
Be aware that calling layoutIfNeeded a bunch of times between drawing cycles can affect performance so only use it when actually needed.

Related

What does cell.layoutifneeded and cell.layoutSubviews and cell.setneedsdisplay method do in general?

What does cell.layoutIfNeeded() and cell.layoutSubviews() and cell.setNeedsDisplay() method do in general ?
layoutSubviews
Lays out subviews. in short this method lets determine the size and position of any subviews, it also helps to get desired behaviour you want for your view if your have a subclass. Documentation here
2.layoutIfNeeded
Forces view to layout immediately,for example you have changed a constraints constant and to reflect the change you need to call layoutIfNeeded.(it can also be animated :P). Documentation here
setNeedsDisplay
Marks the view need to be redrawn in the next drawing cycle, when you call this method, system is notified that view's content is changed and it will be redrawn in next drawing cycle. Documentation here
Please consider reading documentation for better understanding

How would a textView change its frame without calling setFrame

I'm currently debugging an issue in our app with a UITextView getting its last few lines cut off. The UITextView is inside of a collectionView cell that allows itself to be collapsed. I'm trying to catch the execution of the hiding of the text when its frame changes, so I set a symbolic breakpoint on -[UITextView setFrame:], and this will pause a ton of times during setup of the collectionView as expected.
However, when I hit the minimize button, the textView's frame somehow changes it's origin and height, but I never get a call to setFrame: (or at least my breakpoint never gets hit.) I also never get a call to setFrame: when I expand it, when again the origin and height changes.
The only code I call which manipulates the textView directly is hiding and unhiding it. After hiding, I then call the following:
- (void)updateCollection {
[self.collection performBatchUpdates:nil completion:nil];
[self.amountCell updateTable];
[self.collection setNeedsLayout];
[self.collection layoutIfNeeded];
}
The amountCell is unrelated to the cell I'm dealing with, so I don't think it has anything to do with this. Somehow, between hiding the UITextView and then calling a few updating methods on the collectionView, the frame changes without actaully calling setFrame:. How is this possible? How else could the frame be manipulated? Am I missing something obvious here?
One place you might look is in the collection view's -layoutSubviews method, which will be called by the framework whenever it determines that the layout needs to be updated, which you are hinting that it does when you invoke -setNeedsLayout and -layoutIfNeeded. It's an often-overlooked "hidden" influencer, especially with later versions of iOS shifting focus more to view controllers and away from views.

Issue while using viewDidLayoutSubviews

I have heard that viewDidLayoutSubviews is the best place to alter the layout when we have used constraints.
So I jumped to viewDidLayoutSubviews
I have created three UIViews and SubViewed them on SuperView. I am not mentioning the code for frame calculation.
[self.view addSubview:imgMiddleCircle];
[self.view addSubview:imgFirstCircle];
[self.view addSubview:imgLastCircle];
Using this piece of I am adding these circles.
Now when I run my code In viewDidLayoutSubviews I get following screens:
And when I switch to viewWillLayoutSubviews I am getting this on screen:
Why I am getting extra two circles in viewDidLayoutSubviews even I am creating three circles.
And why in viewWillLayout gives the correct Output.
You should code for the fact that viewDidLayoutSubviews is called multiple types.
Ideally addSubview: should be happening in a place like viewDidLoad where you are sure it is only called once.
You can create a flag to avoid calling addSubview: multiples types (not my personal choice)
Otherwise, try to move your set up code to viewDidLoad and force the view to render itself before doing your calculation:
[yourView setNeedsLayout];
[yourView layoutIfNeeded];
Per Apple Documentation,
When the bounds change for a view controller's view, the view
adjusts the positions of its subviews and then the system calls this
method. However, this method being called does not indicate that the
individual layouts of the view's subviews have been adjusted. Each
subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after
the view lays out its subviews. The default implementation of this
method does nothing.
So, essentially, viewDidLayoutSubiews gets called multiple times during the creation of your viewController including in cases like rotating the device, scrolling etc. So, you should be really careful with the code you add to this method, because it might be executed multiple times as well. Use this method to reposition your sub-views and not to add/remove them.
Take a look at this blog for more details.

What conditions can prevent layoutSubviews from being called after setNeedsLayout?

The problem
In a custom view of mine (a UIScrollView subclass) I am calling setNeedsLayout in response to a "reload data" event (triggered by an external source). Most of the time this works correctly and layoutSubviews is called when the next view layout cycle occurs. Sometimes, however, layoutSubviews is not called! Up until now I was living with the "certain knowledge" that setNeedsLayout always triggers layoutSubviews. Apparently I was wrong. I even tried calling layoutIfNeeded after setNeedsLayout, but still no success.
The question
Obviously, I would like to solve my particular problem. On the other hand, I would like to improve my understanding of the view layout process on iOS, so I am formulating my question in a general way: Do you know of any conditions that can prevent layoutSubviews from being called after setNeedsLayout has been called? Answers that focus on UIScrollView are quite welcome, since that is where I am having trouble.
Problem context
I am on iOS 7.1, using Xcode 5.1.1. Notes on the implementation of my custom scroll view:
The scroll view has a single container view of type UIView that is always the same size as the scroll view content size
The scroll view's layoutSubviews implementation places custom subviews into the container view by manually calculating the subviews' frames
The custom subviews' implementation uses Auto Layout
Here is how the reloadData implementation looks like:
- (void) reloadData
{
// Iterates through an internal array that holds the subviews,
// then empties the array. Subviews are deallocated at this
// point.
[self removeAllSubviewsFromContainerview];
self.contentOffset = CGPointMake(0, 0);
// I verified that the content size is always greater than
// CGSizeZero (both width and height)
CGFloat contentWidth = [self calculateNewContentWidth];
self.contentSize = CGSizeMake(contentWidth, self.frame.size.height);
// Here is the problem: Sometimes this triggers layoutSubviews,
// sometimes it does not.
[self setNeedsLayout];
// Adding the following line for debugging purposes does not
// help, making it clear that setNeedsLayout has no effect.
// [self layoutIfNeeded];
}
Last but not least, here are some observations I made:
layoutSubviews is called as expected if the content offset or content size change. layoutSubviews is not called in my particular case if these two values don't change. I first assumed that this observation is a general working principle of UIScrollView, i.e. that layoutSubviews is generally not called for UIScrollView unless content offset or content size change. However, when I wrote a couple of minimal projects I failed to reproduce this assumed "general" behaviour - in those projects layoutSubviews was always called as expected, regardless of whether content offset or content size changed or not.
layoutSubviews is always called as expected if the scroll view displays dummy UILabel instances instead of my own custom subviews
layoutSubviews is always called as expected if I use my own custom subviews, but replace Auto Layout in the custom subviews' implementation by manual frame calculations
The last two observations have led me to believe that Auto Layout is somehow involved, but I really have no idea how this could affect how setNeedsLayout works.

UIViewContentModeRedraw vs setNeedsDisplay?

I don't know the difference between setneedsdisplay and uiviewcontentmoderedraw, when would you use each, aren't they the exact same thing?
They are different things. setNeedsDisplay is a verb. Use it to tell a view that the state of the stuff it's viewing has changed, so it should redraw (by calling its drawRect: method on the next iteration of the run loop).
contentMode is an attribute of a view. It doesn't cause the view to do anything immediately. It specifies how the view handles its content relative to its size. UIViewContentModeRedraw is a value that might be assigned to this property. It means that the view will render size changes by causing itself to redraw (by invoking setNeedsDisplay on itself).
If you plan to animate alteration of your view's size, UIViewContentModeRedraw is an expensive choice because it will try to repeatedly redraw from scratch during the animation (rather than manipulating a bitmap copy).

Resources