I need a little help to understand the viewDidLAyoutSubviewsmethod and how to safely use it.
I need to edit some of my subviews programmatically before the viewDidAppear method. And in order for it to work properly I of course need to wait until the targeted subviews are layed out before I edit them.
Now I thought this was what the viewDidLayoutSubviewsmethod was for, but when I tested it I found out that it was actually called two times before my viewDidAppear method. I tried to edit my subviews only the first time viewDidLayoutSubviews was called, because I just want to run [self editMySubviews] once, but then the targeted subview wasn't ready and it got messed up. This is how I tried:
- (void)viewDidLayoutSubviews {
if (!myBoolean) {
[self editMySubviews];
myBoolean = YES;
}
}
Of course if I remove the if-statement it fixes itself next time the method is called, but I only want [self editMySubviews]to be called once.
So my question is, when I can call the [self editMySubviews] method before the viewDidAppear method, and be 100% sure that all subviews are ready to be edited? Now in my case the viewDidLayoutSubviews gets called two times before viewDidAppear, but will that be the case every time? Is it safe to just call [self editMySubviews] after the second time viewDidLayputSubviews is called?
According to your comment you said
Editing some button constraints according to its superView.frame.size.width which is different on different devices
After viewdidload you will get the proper size from view.bounds
so you can easily set/update constraint and add at the end of this
setNeedsUpdateConstraints and layoutIfNeeded method call
Related
I need to update a constraint programmatically in my project to adjust a view's height accordingly (based on different subviews). I've made outlet of the constraint in my controller but facing an issue.
when I try to update this for the first time (in viewDidLoad or viewWillAppear method), it's not updated. and if i update it afterwards (because of some rotation etc), then it is done correctly. Kindly tell me why this is happening and what is the right way/place to do this? (As i feel that the constraint is updated somewhere again after my updation in viewWillAppear/DidLoad).
I tried view.layoutIfNeeded as well but it didn't help. I guess it has something to do with viewDidLoad and other viewController delegate methods
P.S. I'm also using size classes in my project but I think it has nothing to do with that as it's working in some cases.
Updating constraints may not work in viewWillAppear.
It will, however, work in viewDidAppear.
There are other places you may overwrite, such as: (using static BOOL shouldRefreshView = NO; for the first time)
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if(shouldRefreshView) {
shouldRefreshView = NO;
[self.view layoutIfNeeded];
}
}
I have many places in my code where I need to do stuff based upon the layout of the views in a UIViewController. For example, I have a view in a nib that I then capture in code and use to create a mask for another layer, but I need to wait until the mask view and its subviews have the correct size before I do so.
I have discovered that, although hacky, I can achieve this by creating a counter which increments each time viewDidLayoutSubviews is called, and triggers the code I wish to execute when it has been called for the second time - for some reason, most of the views have yet to properly lay themselves out until viewDidLayoutSubviews is called for the second time.
Like I say, this seems pretty hacky and there's every chance that an iOS update could break my code. Does anyone have a better way of doing this?
- (void)viewDidLoad
{
[super viewDidLoad];
self.layoutCount = 0;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.layoutCount ++;
if (self.layoutCount == 2)
{
// perform final layout code, e.g. create masks
}
}
The safest approach might be to "refresh" the masks every time viewDidLayoutSubviews is called. this way you don't have to worry about whether the views have laid out or not.
How would you add a delay between certain method being called?
This is my code that I want to only trigger 30 times per second:
- (void) scrollViewDidScroll: (UIScrollView*)scrollView {
[self performSelector:#selector(needsDisplay) withObject:nil afterDelay:0.033];
}
- (void) needsDisplay {
[captureView setNeedsDisplay];
}
If I leave it like this, it only gets called after the user stopped scrolling.
What I want to do is call the method when the user is scrolling, but with a delay of 33 milliseconds between each call.
There are different delegate methods which will call on different occausion. This method will only call when user finish scrolling. So you can perform some task if you want to. If you want to do some thing while scrolling or before scrolling you can use different delegate method. Select one of the below depending on your functionality.
– scrollViewDidScroll:
– scrollViewWillBeginDragging:
– scrollViewWillEndDragging:withVelocity:targetContentOffset:
– scrollViewDidEndDragging:willDecelerate:
– scrollViewShouldScrollToTop:
– scrollViewDidScrollToTop:
– scrollViewWillBeginDecelerating:
– scrollViewDidEndDecelerating:
For detail description upon these delegates please follow this link.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/Reference/UIScrollViewDelegate.html
Delegate should call when it should be... other wise you gonna cause some glitch.
Since I couldn't find a solution, and other scroll view delegate methods weren't good, I did it by limiting based on scroll view's content offset, as suggested in a comment. (Ex: only calling it if the user scrolled more then 3 pixels).
This is a decent solution, since I doubt someone can scroll more then 90 pixels per second and STILL read the text in between those 90 pixels.
if (ABS(self.oldOffset.y - webView.scrollView.contentOffset.y) > 3) {
[captureView setNeedsDisplay];
}
The delegate method will get called, you can't setup a limit in that. I think a good option for you is to add the operations in NSOperationQueue, and since you are calling the same selector again and again, make sure you only keep a maximum of 30 operations in the queue at the same time. NSOperationQueue Class Reference.
One cannot have the control over the calling of the delegate methods, and also there is no parameter to set how often delegate method should call or to set the sensitivity of the scrollview,
U cannot control it.
Only thing remaining is to ignoring the call (return at the beginning of the function call if you do not need that) or else process the call..
I have an issue and here how it goes,
I have a view with a subview, the subview is loaded conditionally, only if the parent view is setHidden property is set to YES;
something like [parentView setHidden:YES] and if([parentView isHidden]),
I want to call a method when the orientation changes and that is the cited snippet above, but I have observed that the method shouldAutorotateToInterfaceOrientation is called 4 times during loading and 2 times during runtime, since the method is called more than once, how can I possibly implement a method call ideally since apple's existing method doesn't seem to give me the intuitiveness to put my custom method call with the existing method.
If I would hack this thing, it is possible, but somebody might have a better idea before resorting to things that in the future would just cause me more trouble than benefit.
TIA
Have you tried with
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
// check here for your desired rotation
}
When and how is layoutIfNeeded used? I know that when we change the layout of a view, we can call setNeedsLayout to update the layout but not sure when layoutIfNeeded should be used.
NOTE: I have layoutIfNeeded used in actual code but forgot in what context it was used.
layoutIfNeeded forces the receiver to layout its subviews immediately if required.
Suppose you have overridden layoutSubviews, and UIKit feels that your view requires layout for whatever reason (e.g. you called setNeedsLayout when handling some user action). Then, your custom layoutSubviews method will be called immediately instead of when it would normally be called in the regular UIKit run loop event sequence (after event handling, but before drawRect:).
An example of why you might need to call layoutIfNeeded within a single run loop:
You resize a custom view containing a table view with a custom layout. setNeedsLayout is set so that layoutSubviews will be called later.
A controller object asks the table view to scroll to some particular cell when handling a user event.
Your custom view performs some custom sizing of the table view in layoutSubviews that changes the table view size.
The problem is when the controller asked the table view to scroll (step 2), the table view had bounds that were stale. The updated bounds would only be set on the table view later (step 3). What the controller wanted the table view to scroll to may not actually be visible after layoutSubviews is done. A solution then would be for the controller to call layoutIfNeeded in situations where it knows this might occur.
The difference between these two methods can be now be described by referencing the update cycle.
The method setNeedsLayout for a UIView tells the system that you want it to layout and redraw that view and all of its subviews, when it is time for the update cycle. This is an asynchronous activity, because the method completes and returns immediately, but it isn’t until some later time that the layout and redraw actually happens, and you don’t know when that update cycle will be.
In contrast, the method layoutIfNeeded is a synchronous call that tells the system you want a layout and redraw of a view and its subviews, and you want it done immediately without waiting for the update cycle. When the call to this method is complete, the layout has already been adjusted and drawn based on all changes that had been noted prior to the method call.
So, stated succinctly, layoutIfNeeded says update immediately please, whereas setNeedsLayout says please update but you can wait until the next update cycle.
LayoutSubViews() - Don’t call directly, instead call
setNeedsLayout(),override if constraint base not offer expected
behaviour.
SetNeedsLayout()- Call on main thread, it wait for next drawing cycle.
good for performance.
LayoutIfNeeded() - Layout subviews immediately.
setNeedsLayout actually calls layoutIfNeeded, so if your calling setNeedsDisplay there's no reason to call layoutIfNeeded. In this way setNeedsLayout is a convenience method for calling layoutIfNeeded which does the heavy lifting.