How to make the method: layoutsubviews that synchronously excute? - ios

I made a sub class TKButton of UIButton and provided a property hasUpdate of type BOOL.
I tried to overwrite its setHasUpdate method and initialized the new view which is not configured frame while having red color.
When hasUpdate is Yes, make the hidden = No else Yes.
At last I used setNeedsLayout method. In this method layoutSubvew, tried to change the new view frame.
If I set hasUpdate = YES , I hope the _badgeView appeared. However,it seems that it does not appear immediately. It might be the setNeedsLayout is asynchronies.
Does anyone has the idea about this problem?
Thanks.

Related

When can you call UIEdgeInsets safeAreaInsets?

I need to adjust my views for iPhone X but I can’t figure out when the safeAreaInsets are initialized. According to the documentation,
If the view is not currently installed in a view hierarchy, or is not
yet visible onscreen, the edge insets in this property are 0.
I would think that when viewDidLoad is called, that the values would be set, but that is not the case. I can get values when viewDidLayoutSubviews is called, but that seems to be too late and doesn’t return the correct values anyway.
Can anyone explain how to use the safeAreaInsets property to me?
They are initialized before view is layouted (hence within view hierarchy). Best place to access them and act accordingly is within viewWillLayoutSubviews method. As you have already mentioned viewDidLayoutSubviews is little too late, but willLayoutSubviews works just fine. Happy coding!

touchesBegan() not being called on subviews

I have a class called SKButton, subclass of SKSpriteNode, which implements the touchesBegan() function to print("hello"). But when i add an SKButton object to my view, touchesBegan() never gets called on the button. Why?
You need to make sure that userInteractionEnabled is enabled on all of the superviews of your view.
Also keep in mind that coming from Swift 2.3 to Swift 3 in your project might trigger the warning for the name of the method, you update it using the default fix and then you end up like me wondering why the methods related to touches are not called despite of the fact that you set userInteractionEnabled. Fix: make sure that method name is written as per your current version of Swift!

When can I activate/deactivate layout constraints?

I've set up multiple sets of constraints in IB, and I'd like to programmatically toggle between them depending on some state. There's a constraintsA outlet collection all of which are marked as installed from IB, and a constraintsB outlet collection all of which are uninstalled in IB.
I can programmatically toggle between the two sets like so:
NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)
But... I can't figure out when to do that. It seems like I should be able to do that once in viewDidLoad, but I can't get that to work. I've tried calling view.updateConstraints() and view.layoutSubviews() after setting the constraints, but to no avail.
I did find that if I set the constraints in viewDidLayoutSubviews everything works as expected. I guess I'd like to know two things...
Why am I getting this behavior?
Is it possible to activate/deactivate constraints from viewDidLoad?
I activate and deactivate NSLayoutConstraints in viewDidLoad, and I do not have any problems with it. So it does work. There must be a difference in setup between your app and mine :-)
I'll just describe my setup - maybe it can give you a lead:
I set up #IBOutlets for all the constraints that I need to activate/deactivate.
In the ViewController, I save the constraints into class properties that are not weak. The reason for this is that I found that after deactivating a constraint, I could not reactivate it - it was nil. So, it seems to be deleted when deactivated.
I do not use NSLayoutConstraint.deactivate/activate like you do, I use constraint.active = YES/NO instead.
After setting the constraints, I call view.layoutIfNeeded().
Maybe you could check your #properties, replace weak with strong.
Sometimes it because active = NO set self.yourConstraint = nil, so that you couldn't use self.yourConstraint again.
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}
I believe the problem you are experiencing is due to constraints not being added to their views until AFTER viewDidLoad() is called. You have a number of options:
A) You can connect your layout constraints to an IBOutlet and access them in your code by these references. Since the outlets are connected before viewDidLoad() kicks off, the constraints should be accessible and you can continue to activate and deactivate them there.
B) If you wish to use UIView's constraints() function to access the various constraints you must wait for viewDidLayoutSubviews() to kick off and do it there, since that is the first point after creating a view controller from a nib that it will have any installed constraints. Don't forget to call layoutIfNeeded() when you're done. This does have the disadvantage that the layout pass will be performed twice if there are any changes to apply and you must ensure that there is no possibility that an infinite loop will be triggered.
A quick word of warning: disabled constraints are NOT returned by the constraints() method! This means if you DO disable a constraint with the intention of turning it back on again later you will need to keep a reference to it.
C) You can forget about the storyboard approach and add your constraints manually instead. Since you're doing this in viewDidLoad() I assume that the intention is to only do it once for the full lifetime of the object rather than changing the layout on the fly, so this ought to be an acceptable method.
You can also adjust the priority property to "enable" and "disable" them (750 value to enable and 250 to disable for example). For some reason changing the active BOOL didn't had any effect on my UI. No need for layoutIfNeeded and can be set and changed at viewDidLoad or any time after that.
The proper time to deactivate unused constraints:
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.myLittleConstraint.active = NO;
}
Keep in mind that viewWillLayoutSubviews could be called multiple times, so no heavy calculations here, okay?
Note: if you want to reactive some of the constraints later, then always store strong reference to them.
When a view is being created the following life cycle methods are called in order:
loadView
viewDidLoad
viewWillAppear
viewWillLayoutSubviews
viewDidLayoutSubviews
viewDidAppear
Now to your questions.
Why am I getting this behavior?
Answer: Because when you try to set the constraints on the views in viewDidLoad the view does not have its bounds, hence constraints cannot be set. It's only after viewDidLayoutSubviews that the view's bounds are finalized.
Is it possible to activate/deactivate constraints from viewDidLoad?
Answer: No. Reason explained above.
I have found as long as you set up the constraints per normal in the override of - (void)updateConstraints (objective c), with a strong reference for the initiality used active and un-active constraints. And elsewhere in the view cycle deactivate and/or activate what you need, then calling layoutIfNeeded, you should have no issues.
The main thing is not to constantly reuse the override of updateConstraints and to separate the activations of the constraints, as long as you call updateConstraints after your first initialization and layout. It does seem to matter after that where in the view cycle.

setText method for UITextView only works from the second time since the method is called.

I'm having a problem with setText method for UITextView.
As I said on the title, I tried to change a UITextView text by using setText method or change the text property directly. It only works from the second time since the method is called.
My UITextView was an outlet. I even tried to change it's text directly from the owner class or create a method to call from another class, but it behaves the same.
I dont know if I'm doing sth wrong when create it as an outlet, I also tried to set it as nonatomic, strong, weak, retain but I still can't get it.
Any advice for my case? Thanks in advance! :)
EDIT:
I figured it out from David H's answer.
As my app is using tab, first tab is used for searching words, the second one for displaying the meaning, I tried to set the text before the outlet is created (as I haven't clicked the second tab yet). If I click the tab meaning first in order to let the outlet to be created, then it works perfectly.
Thanks for all the answer!
Almost for sure, the first time you try to set it the outlet is nil - not yet set. So add an assert (assert(myTextView) before you set it, or at least a NSLog message. You will surely find that the textView is nil the first time you try.

iOS passing property values to initialized views

I have a UIView subclass. The view has a float property. I initialize this subclass from another view (it's superView) like so:
myView = [[CustomView alloc]initWithFrame:aFrame];
The problem is, I am confused about how to assign values to myView's float property. When I try to assign a value just after initializing (from the superview's initWithFrame), nothing happens. Even when I try to to assign from another method or from the viewController it does not work.
Thanks for reading!
Most likely your class is trying to use it in a method that is called before you set it. Make a custom initializer such as -(id)initWithFrame:(CGRect)frame andFloat:(float)f. This will allow you to set the float when the object is created, so it will have that value upon instantiation. If this is not the issue, we will need to see more code!

Resources