how to invalidate uiview constraints - iphone

I would like to invalidate a uiview constraints from code to force all fields to relayout based on a specific change in the view.
For example, suppose i have two UILabels that are given a fixed VerticalSpace constraint. And in some user event i am resizing the label that is on top. I would like to be able to force the constraint to invalidate from code.
This is the first time i use constraints and i do not understand how i can do this from code. I know that when the device is rotated constraints on UIViews are invalidated.

You call setNeedsLayout on the relevant view, typically your view controller's .view. :
[self.view setNeedsLayout];
You're not really invalidating the constraints, that is a slightly different thing used for custom views which may need to remove and recalculate constraints based on other changes. What you're doing is letting the view know that some of the factors it used to calculate its layout have now changed. The constraints are still valid, but the results have changed.
To animate to your new layout, you can call layoutIfNeeded inside an animation block.

Related

Supporting both AutoLayout and traditional layout

I have a custom subclass of UIButton that I want to re-use in multiple projects, some of which use interface builder and storyboards, and some of which may not. It involves setting the button's frame in several places.
If I add
self.translatesAutoresizingMaskIntoConstraints = YES;
in the initialization, everything looks and works right, however, I get:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
Will attempt to recover by breaking constraint…etc
logged to the console. However, if I set
self.translatesAutoresizingMaskIntoConstraints = NO;
none of the button sizing works and the button is completely broken.
Is there a way I can suppress the error since it seems to degrade to the right thing or can I somehow manually remove the offending constraint?
I've tried dumping the constrains after the button is initialized, however, it does not seem to contain the constraint AutoLayout ends up breaking.
Edited: Below are links to a sample project and then my button code. It works great when auto layout is turned off in the storyboard, however, when auto layout is turned on, the buttons seem to position at the origin (seems to be because when initWithCoder is called, the buttons don't have superviews or any position information).
http://www.kudit.com/dump/KFB.zip
http://www.kudit.com/dump/KuditConfirmButton.zip
Your guess is correct, in that when the buttons are initialized from the storyboard (using initWithCoder:) the frame is zero - this is expected for views using autolayout - frames don't get assigned until later on.
I have your sample project mostly working under autolayout by creating a boolean ivar called hasSetup, which get set to YES inside the setup method. The setup method doesn't run if the frame is zero. I also call it again from layoutSubviews. This gets around the not-working-when-using-autolayout in storyboard problem.
The various layers still aren't the correct size though. You may want to look into resizing them from layoutSubviews.
You also can't use setFrame under autolayout - you need to make sure any constraints on your view (like the sizing constraints from the storyboard) are adjusted instead. It's going to get quite messy dealing with both possibilities.
They look really good though - hope to see them on GitHub when you've sorted these problems.

Automatically reposition views after UITextview resizes - iOS

For a simple example lets say I have a UITextView with a Button underneath it. These two controls are siblings both embedded in a parent UIView.
I now change the text within the UITextView and resize it accordingly. Is there a way to make the button automatically move to have the same relative distance to the bottom of the UITextView? This may seem trivial for this case, but I will have a longer hierarchy with multiple UITextViews that change and it would be nice not to have to calculate the height of every object manually.
One way to approach this would be with a table view: if you place each of your text views within its own table view cell, the table view will automatically calculate its total height from individual calls to its delegate’s -tableView:heightForRowAtIndexPath: method and lay itself out accordingly. Whenever the height of one of your text views needs to change, you can call -reloadData on the table view to make it lay itself out again. A disadvantage of this approach is that it’s really difficult to animate the height changes; if that’s essential to the effect you’re going for, I’m afraid you’re stuck with doing the entire layout manually.
Try the autoresizingMask property of UIView.

Cocoa-Touch: How to do layouting

I have a view (UIScrollView), which loads some data, and displays various things from it in various subviews. So I have approx 10 subviews (UIImageView, UILabel) and I need to place them programatically considering their unpredictable contents (i.e. different height/width for the UILabels depending on the text property).
From what I've read, there is no layout framework for Cocoa-touch.
What is the best way to do this?
From what I can tell, I should put the contents in the views, then start calculating coordinates based on their frames after calling their sizeToFit methods.
This approach is very error-prone. Is there really no other way?
You are right, there are no automatic layout managers. Subclassing UIScrollView and overriding layoutSubviews is probably the right way to implement your custom algorithm. You can then call setNeedsLayout to do the layout.
Layout in Cocoa is typically done with auto-resizing (using autoresizingMask). You start with your view at some hard-coded initial size, say 200x200; place your subviews onto this view and set the autoresizing flags accordingly. This view is then free to be resized to its actual size, as determined by its parent view/window. The process is the same whether you use Interface Builder or whether you do it programmatically.
If you need a vertical stack of views you can use a table view.
If you want more complicated layout you need to implement it yourself, by overriding layoutSubviews.
I'm not aware of any automatic layout managers or the like.
So, I think you'll have to calculate the desired positions and sizes and update the frames of your subviews manually.
EDIT: I found this question where Brad Larson points to an example of a custom layout manager. HTH
You can use Interface Builder to create a view and then drag and drop elements into it.

How to resize other subviews and parent view when one subview size changes

I have a view which is similar to mail app. I have a scroll view and in that many other subviews. When the user clicks on any textview, it expands to show the text in it. e.g. "to" field in mail. I would want to resize the other subviews such that it starts below the expanded region. How can I do that.
I created the view through IB and I marked it for flexible top margin to facilitate this. But nothing happens automatically and hence I was wondering if I need to call sizethatFits/setneedsLayout.
When the subview resizes, call [[self superview] setNeedsLayout]. In your superview, implement -layoutSubviews to do the actual layout. You'll have to calculate everything yourself. Fixed/flexible margins are relevant to resizing the superview, not on peer views. The default -layoutSubviews does nothing at all; it just gets called at appropriate times.
If you need to force layout to happen at a particular point, then you can call -layoutIfNeeded on yourself or your superview. Read the docs on how this method works. Generally speaking you don't need to call this, though. It will usually get called at the appropriate time if you just use -setNeedsLayout.

Nested UIScrollView does not paint

I am building an application which allows the user to view and edit data on a daily basis. To accomplish this i have created a view in Interface builder which contains all of the daily data, and i "tile" this view within an appropriately sized UIScrollView to allow the user to swipe between the days. The view contains two UITextFields, a couple UILabels, and a UITextView. At any given time i have five instances of this view which i re-position around and update the data for the appropriate position.
Everything works great with the exception of the UITextView. The UITextView does not display its Text property until the user has interacted with it (I'm guessing this triggers a redraw).
Is there an easy way to get the UITextView to "paint" its content when its not on screen (but still added as a subview to another view)?
One thing you can try, which is what I just did to fix the same problem, is hook into the parent scrollview's scrollViewDidScroll delegate function. Then in that function, send a message to the UITextView that would force a redraw, my method was setting its frame to the same frame. It's pretty brute force but it's better than nothing.