Auto Layout how to move the view2 from right to the bottom? - ios

I have two views in the cell at the left UIImageView and at the right UIView Containing some other views as in picture
Now I want if the width of Cell is => 300 then View2 should be at the right of Screen as it is in picture other wise it should be moved to the bottom of View1

Do one thing. Set another constraints for that view
1) leading to main view
2) top to imageview (you can give this constraint by right click from view to drag imageview then vertical spacing.)
Now give outlets for this two constraints programatically.
And also give outlets for constraints which are:
1) leading from view to imageview
2) top of view.
Now as per your requirement,set if condition
e.g.
if(width => 300)
{
topOfViewToImageviewConstraint.isActive = false
leadingOfViewToMainViewConstraint.isActive = false
}
else
{
leadingOfViewToImageViewConstraint.isActive = false
topOfviewToMainView.isActive = false
leadingOfViewToMainViewConstraint.constant = 0
topOfViewToImageviewConstraint.constant = 20
}

This will work. I am not currently in Xcode, so this is just some sample code, but the idea will work.
in cellForRowAt
check if self.tableView.cellForRowAt(IndexPath.row).frame.width => 300
if it is, make no change, but if it is not find the y coordinate of the bottom of view 1 and make a new rect at (x: 0, y: botOfView1, width, height). Then set the frame of view 2 equal to that.

Related

How to corrently use UIViews systemLayoutSizeFitting to get the height to show all subviews using a given width?

TD;DR
It seems that in some cases systemLayoutSizeFitting does not return the correct height to correctly show / position all subviews of a view. Am I using systemLayoutSizeFitting wrong or is there some other way to avoid this?
Long story:
The XIB file of a UIViewController does not only contain the main view but also a number of other views which are added to the view controllers view at runtime. All these additional views should get the same height when they are added to the view controllers view.
The views might look like this: A simple container view holding some subviews which are stacked on top of each other.
Since the height of the container view should be flexible, the vertical spacing between the bottom button and the lable above it, uses a grater-than constraint.
To give all views the same height, I tried to measure the necessary height of each view using systemLayoutSizeFitting:
#IBOutlet var pageViews: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
var maxHeight: CGFloat = 0
for pageView in pageViews {
// Add pageView somewhere on view and give it leading, trailing and top
// constraint, but no height constraint yet.
addToView(pageView)
maxHeight = max(maxHeight, pageView.systemLayoutSizeFitting(CGSize(width: view.frame.width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel).height)
}
for pageView in pageViews {
// Give all pageViews the same height
pageView.heightAnchor.constraint(equalToConstant: maxHeight).isActive = true
}
}
This does not work, when the label text becomes to long:
In the right example the height is not large enough and thus the button is squeezed. I can counter act this by raising the vertical compression resistance of the button, however in this case the other controls (e.g. the title label) is squeezed...
Why is this? Why does not systemLayoutSizeFitting return a height which is sufficent to show all controls without any squeezing?
Its actually smash button's height when label text is getting bigger . You are setting top and bottom constraints but button height is not declared so when label getting bigger , view basically say "I can reduce button height before updating my height , I have space.Bottom and top constraints are still same , didn't effect."
Giving the constant height constraints of button might be fix your issue.
If you want your view to resist to compression you should use the defaultHigh priority as a verticalFittingPriority instead of fittingSizeLevel.

UIScrollView with dynamically sized content

(Xcode 11, Swift)
Being a newbie to iOS and Autolayout, I'm struggling with implementing a fairly simple (IMHO) view which displays a [vertical] list of items. The only problem is that items are decided dynamically and each of them could be either text or image (where either of those could be fairly large so scrolling would be required). WebView is not an option, so it has to be implemented natively.
This is how I understand the process:
Make in IB a UIScrollView and size it to the size of the outer frame.
Make a container view as a subview of UIScrollView (again, in IB) and size it the same.
Set constraint on equal width of both
At runtime, populate container view with UILabels/UIImageViews and also set constraints programmatically to ensure proper layout.
"Tell" scrollview about the subview height in order to make it manage the scrolling thereof.
Is this the right approach? It doesn't seem to work for me (for a toy example of dynamically adding a very tall image to a container view - I cannot get the scrolling to work). What would be the proper way to do the last step in the process above - just force the contentSize of the scrollview to the size of the populated container view (it doesn't seem to work for me). Any help would be appreciated.
When adding multiple elements to a scroll view at run-time, you may find it much easier to use a UIStackView... when setup properly, it will automatically grow in height with each added object.
As a simple example...
1) Start by adding a UIScrollView (I gave it a blue background to make it easier to see). Constrain it to Zero on all 4 sides:
Note that we see the "red circle" indicating missing / conflicting constraints. Ignore that for now.
2) Add a UIView as a "content view" to the scroll view (I gave it a systemYellow background to make it easier to see). Constrain it to Zero on all 4 sides to the Content Layout Guide -- this will (eventually) define the scroll view's content size. Also constrain it equal width and equal height to the Frame Layout Guide:
Important Step: Select the Height constraint, and in the Size Inspector pane select the Placeholder - Remove at build time checkbox. This will satisfy auto-layout in IB during design time, but will allow the height of that view to shrink / grow as necessary.
3) Add a Vertical UIStackView to the "content view". Constrain it to Zero on all 4 sides. Configure its properties to Fill / Fill / 8 (as shown below):
4) Add an #IBOutlet connection to the stack view in your view controller class. Now, at run-time, as you add UI elements to the stack view, all of your "scrollability" will be handled by auto-layout.
Here is an example class:
class DynaScrollViewController: UIViewController {
#IBOutlet var theStackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// local var so we can reuse it
var theLabel = UILabel()
var theImageView = UIImageView()
// create a new label
theLabel = UILabel()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theLabel.translatesAutoresizingMaskIntoConstraints = false
// multi-line
theLabel.numberOfLines = 0
// cyan background to make it easy to see
theLabel.backgroundColor = .cyan
// add 9 lines of text to the label
theLabel.text = (1...9).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .yellow
// add 5 lines of text to the label
theLabel.text = (1...5).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// create a new UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load an image for it - I have one named background
if let img = UIImage(named: "background") {
theImageView.image = img
}
// let's give the image view a 4:3 width:height ratio
theImageView.widthAnchor.constraint(equalTo: theImageView.heightAnchor, multiplier: 4.0/3.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .green
// add 2 lines of text to the label
theLabel.text = (1...2).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load a different image for it - I have one named AquariumBG
if let img = UIImage(named: "AquariumBG") {
theImageView.image = img
}
// let's give this image view a 1:1 width:height ratio
theImageView.heightAnchor.constraint(equalTo: theImageView.widthAnchor, multiplier: 1.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
}
}
If the steps have been followed, you should get this output:
and, after scrolling to the bottom:
Alignment constraints (leading/trailing/top/bottom)
The alignment constraint between Scroll View and Content View defines the scrollable range of the content. For example,
If scrollView.bottom = contentView.bottom, it means Scroll View is
scrollable to the bottom of Content View.
If scrollView.bottom = contentView.bottom + 100, the scrollable
bottom end of Scroll View will exceed the end of Content View by 100
points.
If scrollView.bottom = contentView.bottom — 100, the bottom of
Content View will not be reached even the scrollView is scrolled to
the bottom end.
That is, the (bottom) anchor on Scroll View indicates the (bottom) edge of the outer frame, i.e., the visible part of Content View; the (bottom) anchor on Content View refers to the edge of the actual content, which will be hidden if not scrolled to.
Unlike normal use cases, alignment constraints between Scroll View and Content View have nothing to do with the actual size of Content View. They affect only “scrollable range of content view” but NOT “actual content size”. The actual size of Content View must be additionally defined.
Size constraints (width/height)
To actually size Content View, we may set the size of Content View to a specific length, like width/height of 500. If the width/height exceeds the width/height of Scroll View, there will be a scrollbar for users to scroll.
However, a more common case will be, we want Content View to have the same width (or height) as Scroll View. In this case, we will have
contentView.width = scrollView.width
The width of Content View refers to the actual full width of content. On the other hand, the width of Scroll View refers to the outer container frame width of Scroll View. Of course, it doesn’t have to be the same width, but can be other forms like a * scrollView.width + b.
And if we have Content View higher (or wider) than Scroll View, a scrollbar appears.
Content View can not only be a single view, but also multiple views, as long as they are appropriately constrained using alignment and size constraints to Scroll View.
For details, you may follow this article: Link.

How to resize the superview to conform to the subviews constraints

I created a custom UIView class where I create 2 UILabels and 1 UIImageView. I set the constraints for each element. Since the Labels will hold dynamic text each view will have a different height. How can I resize the height of the view? the constraints (top to bottom) are:
View Top > 16 > Label1 > 8 > ImageView > 8 > Label2 > 16 > View Bottom
The problem is: I have to initialize the UIView with a frame. and no matter what I try the view always uses the height of the frame given by initializing. And I cannot calculate the height of all elements and set a new frame because the labels habe sometimes 1 and sometimes 5 lines.
This is how the view looks at runtime
This is how its supposed to look like
Thanks in advance
You have to initialize the view with a frame, but when setting translatesAutoresizingMaskIntoConstraints to false after initializing, the frame passed in becomes irrelevant. Therefore, you can do what #BallpointBen recommended in the comments and pass in .zero:
let newCustomView = CustomView(frame: .zero)
newCustomView.translatesAutoresizingMaskIntoConstraints = false
// auto layout constraints should now determine height

Re-position uilabel

I wanted the "number" & "status" to display on the left if there is not QRCode display (constraints has been set for image & labels.)
Result without QRCode:
.
The result with QRCode will be:
IT IS very easy just make width Constraint of QRcode image, if QR code is not available than make widht constant = 0,
#IBOutlet weak var width: NSLayoutConstraint!
if (your condition)
{
width.constant = 0
}
else
{
width.constant = 30
}
You can do that with a second pair of constraints attached to left side of a view.
Add outlets to that constraints and set constant value to desired offset(from code), if there is no QRCode View.
Another way is to add MoreThanOrEquel constraint to left side (e.g >= 10)
Than another constraint with constant value 10, but with lower priority.
When you remove QRCode view, it will destroy it constraints and constraints to left side will affects and move yours labels to left.
You could make constraint for labels that way:
I.e. make constraint for image leading to left side + image width, both labels also have leading to left side. Then if there is no QR Code adjust labels leading constraints to value you need.
Or you could make both labels leading to image view and set the image view width constraint to 0.
This Problem is very simple , can achieve using UIStackView just making hide/Show.
For Ex: If QR-code is present show or else hide QR-Code.
Label (Number and status and image) will automatically get adjust. Following is the screen short for better understanding.
IBOutlet UIView *qr_view; // Image or View
Case 1 :QR Present
qr_view.hidden = NO;// Show
Case 2: QR Not Present
qr_view.hidden = YES;// hide

Updating constraints programmatically

Situation is like I have a view with four buttons
There comes a condition when button with text Search Community should be hidden and width of Options button should get increased
Using Constraints for the first time in my project, I am not getting how to achieve this. Set of constraints added on both these buttons would be clear from the following two images.
The problem I see with your setup is that you have this constraint between the 2 buttons of equal width. This is true in the first case but when you want to hide one of them, it is not anymore.
So, you will need to rethink your constraints a bit. Maybe instead of the equal width constraint, you could use a static width constraint one the first button(the left one, that you want to hide). Then the second one, will just have a horizontal space constraint to the first one, and a trailing space to the superView.
Then you make an outlet in the VC for the width constraint of the first button and when you want to hide it you do something like this:
self.searchButtonWidthConstraint.constant = 0
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.view.layoutIfNeeded() // if you want to animate the constraint change
})
Let me know if you have questions. Good luck and hope it works out!
Update
For the landscape use case, you could listen to the orientation change notification, to update the width constraint of the button
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.searchButtonWidthConstraint.constant = LANDSCAPE_WIDTH
UIView.animateWithDuration(duration, animations: { () -> Void in
self.view.layoutIfNeeded() // if you want to animate the constraint change
})
}
Add constraint to 1st Button
Leading Space To NewView(gray colored)
Bottom space to NewView
Top Space to Topbutton (blue colored in your 1st and 2nd image)
Width (from Bottom - Right portion )
Add Constraint to 2nd Button
Trailing Space to NEwView
Bottom space to NewView
Top Space to Topbutton (blue colored in your 1st and 2nd image)
Horizontal space FirstButton ( Search Community )
Width

Resources