how to change view controller width size programatically? - ios

I am trying to make a viewcontroller manually without using pod following this tutorial https://www.youtube.com/watch?v=Hl_Re_KLhcY&t=141s
but the sideMenuVC (the green one) has width that set accurately, I mean, the sideMenuVC should be 80% of the size of MainMenuVC. so if I change from iPhone 5s to iPhoneX, the autolayout still look perfect. But this is what I get
the sideMenuVC should be 80% only, but it looks full size on iPhone 5s
in sideMenuVC viewDidLoad I want to set something like this
class SideMenuVC: UIViewController {
#IBOutlet var sideMenu: UIView!
override func viewDidLoad() {
super.viewDidLoad()
sideMenu.frame.width = self.view.frame.width / 2
}
}
but it doesn't work since it is get only property
so I want to change the width in here

What you would optimally want to achieve your desired output is keep the view controller's width as is, but add a normal view inside with the following constraints
Leading to superview with a constant value of 0
Top to superview with a constant value of 0
Bottom to superview with a constant value of 0
Width equal to superview with a multiplier value of 4:5
You also need to set the background color of the UIViewController's default view property to .clear.
Finally, set the presentation style of that newly created UIViewController to Over Current Context.
When presenting a view controller using the UIModalPresentationFullScreen style, UIKit normally removes the views of the underlying view controller after the transition animations finish. You can prevent the removal of those views by specifying the UIModalPresentationOverCurrentContext style instead. You might use that style when the presented view controller has transparent areas that let underlying content show through.

Related

How to retain constraints for a Stack View's child after unhiding?

There's a Stack View which contains three labels and has the following constraints:
height = 300
top = Safe Area + 50
trailing/leading = 0
and the following attributes:
Axis -> Vertical
Alignment -> Center
Distribution -> Equal Spacing
Label 3 (blue) has a variation: for Compact height size class Installed attribute is disabled (configured via Attributes Inspector). This makes it hidden in the horizontal orientation on iPhone:
When the app starts all the labels have correct locations on the screen. After rotating to horizontal orientation and back, Label 3 placed in the top left corner of the Stack View while other labels are aligned correctly:
Xcode View Hierarchy debugger reveals that after reappearing Label 3 doesn't have any UIStackView related constraints and the warning next to it says "Position is ambiguous":
It seems that the Label 3 have lost all its constraints related to Stack View after being hidden and shown again.
You can't use the installed attribute for that, since that adds/removes views to the superview. This is not good enough for a StackView, since it requires subviews to be added using addArrangedSubview().
An easy solution is to create an outlet for your label, and hide/show it upon rotation:
#IBOutlet private var label3: UILabel!
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
label3.isHidden = newCollection.verticalSizeClass == .compact
}
A much, much easier method - set trait variations on the Hidden property.
Here is your layout:
Select the bottom label, and in the Attributes Inspector pane, click the + button next to Hidden:
Change the Variation to:
You now have a new Hidden variation which you can select:
and here's what you get when rotated to wC hC:
As these images show, you even see the results in Storyboard... no waiting for code at run-time.

Rounded UIImageView

I'm trying to get a rounded UIImageView but it seems to render differently on different devices;
Looks like this on an iPhone Xr:
Looks like this on an iPhone 7:
I have a height constraint of 60 and the following code:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.userAvatar.layer.cornerRadius = self.userAvatar.frame.height / 2
self.userAvatar.layer.masksToBounds = false
self.userAvatar.clipsToBounds = true
self.userAvatar.layer.borderWidth = 0
}
Any ideas?
It seems to me that you have given the image leading and trailing constraints instead of a fixed width.
To achieve a circle give image view width equal to height.
This happens due to different widths of devices.
If you're managing this view using Interface Builder (i.e. Storyboard or XIB), you can enforce a square shape (which becomes a circle when combined with the rounded corners you already have) for the view directly from there by defining a constraint for its Aspect Ratio. No need to code anything.
Control-drag (like you do to create Outlets, Actions, etc.) from the image view to itself, and the following popup will appear.
Select Aspect Ratio, which will create a constraint matching whatever the view's current ratio is (in this example, it's 15:8). If the view was already square, the constraint created should already be correct.
If not, you can find that constraint by clicking the following icon (for the Size inspector):
From there, you can double-click on that constraint to edit it, and change the Multiplier to 1:1:
In fact, an even easier option is, once you've Control-dragged from the view to itself, hold down Alt/Opt and the option displayed in the popup will change to Aspect Ratio (1:1), meaning you can set it directly from there without even having to edit the constraint.
Constrain the height equal to the width.
And, create a simple UIImageView subclass:
class RoundedImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height / 2
}
}
The frame can (and will) change based on view lifecycle. By updating the cornerRadius in layoutSubviews() it will keep it "round".

Change View Position based on ImageView size

I have Imageview and UIView on View Controller. If Imageview is nil or image is not available then UIView replace its postion.Do any know how is it possible using auto layout?
For trying purpose, I have fixed height and width of both(Imageview and UIView). Imageview have "top 8 pixel" and "Horizontally in container" margin. UIView have "top 0 from Imageview" and "Horizontally in container" margin. Set Imageview to nil but it doesn't work.
A good suggestion would be to add both of the image view and the view in a stackView and follow the steps mentioned in: UIStackView Distribution Fill Equally.
However, you can achieve what are you asking for by adding additional constraint between the bottom view and the top layout guide:
and then, set its priority value to be less than the default (1000) -in my example I set it to 500- and the its constant value to 0:
Its appearance should be displayed as dotted line, meaning that there is another constraint -with a higher priority value- deciding the x axis of the view, if this constraint has been removed/deactivated the dotted one should be activated.
Finally, make sure that if there is no available image you have to remove image view from its super view (call imageView.removeFromSuperview()), setting it as hidden or setting its alpha to 0.0 doesn't activate the dotted constraint:
class ViewController: UIViewController {
//...
#IBOutlet weak var imageView: UIImageView!
// I'm checking in 'viewDidLoad' method just for describing purposes,
// of course, you can do the check when needed...
override func viewDidLoad() {
// if there is something wrong, you should call:
imageView.removeFromSuperview()
}
//...
}
The output would be:
Take the outlet of height constraint of UIImageView and do below code
if (imageview == nil){
imageViewHeightConstraint.constant = 0
}
else{
imageViewHeightConstraint.constant = 60
}
And other query you can ask.
You can manage this by giving priority to the constraints using auto layout. Give your UIView top space from ImageView and the TopLayout. Assign a lower priority to the constraint (any value less than 1000) where you have given top space from top layout guide. If your image is nil , remove the image view from the view using imageview.RemoveFromSuperView(), the UIView will automatically take the next constraint i.e. from TopLayout guide. I am attaching a screenshot where to find the priority of the constraint on storyBoard.

Adding UIStackView as a empty container in Storyboard

My wish is to have a UIStackView added in the Storyboard with 0 height that I reference in the code to add subviews to programatically. However, Storyboard is complaining about that it doesn't have a height (I haven't set it, so it is correct that it warns me). I only want it to act as a dynamic container for other views. The UIStackView I am talking about is added as a subview inside another UIStackView.
It is the stackview below "Name Label" it is all about.
Select your StackView which you want to set height 0
Select Size inspector
Select Intrinsic Size property of StackView
Change it to Placeholder from Default
Now change StackView Height as you wish!! even 0.
You can hide you inner stack view using the boolean .hidden property. Create an outlet to the inner stack view, say innerStackView, and hide it at initialization using innerStackView.hidden = true, e.g.:
#IBOutlet weak var innerStackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// ...
innerStackView.hidden = true
}
This will hide the stack view even if it contains several other views; hence, it can act as your hidden dynamic container, and you needn't fiddle around with height properties. If you want to show the view again, simply bitswap the .hidden property to innerStackView.hidden = false.

Where to update Autolayout constraints when size changes?

I have several UIViews laid out along the bottom of a containing UIView. I want these views to always be equal width, and always stretch to collectively fill the width of the containing view (like the emoji keyboard buttons at the bottom). The way I'm approaching this is to set equal widths to one of the views, then just update the width constraint of that view to be superviewWidth / numberOfViews which will cause all of the other views to update to that same value.
I am wondering where the code to change the constraint constant needs to go. It needs to be set before the keyboard appears on screen for the first time and update when rotating the device.
My first attempt at a solution was to place it in updateViewConstraints and calculate the width via containerView.frame.size.width. But this method is called twice upon load, the first time it calculates the values correctly, but the second time for some reason the containerView's width is 0.0. Another issue is that when rotating, the containerView's width is not the value that it will be after rotation, it's the current value before rotation. But I don't want to wait until after the rotation completes to update the constraint, because the buttons will be the original size then change which will be jarring to the user.
My question is: where is the most appropriate place to put this code? Is there a better way to calculate what the width will be? I can guarantee it will always be the exact same width as the screen width. And I am using Size Classes in Xcode 6, so willRotateToInterfaceOrientation and similar methods are deprecated.
On all classes that implement the UITraitEnvironment protocol the method traitCollectionDidChange will be called when the trait collection changes, like on rotation. This is the appropiate place to manually update the constraints when using the new Size Classes. You can also animate the transition with the method willTransitionToTraitCollection
Basic example:
class ViewController: UIViewController {
var constraints = [NSLayoutConstraint]()
func updateConstraintsWithTraitCollection(traitCollection: UITraitCollection) {
// Remove old constraints
view.removeConstraints(constraints)
// Create new constraints
}
override func willTransitionToTraitCollection(newCollection: UITraitCollection!,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!) {
super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext!) in
self.updateConstraintsWithTraitCollection(newCollection)
self.view.setNeedsLayout()
}, completion: nil)
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection!) {
updateConstraintsWithTraitCollection(traitCollection)
}
}
Besides that I want to recommend Cartography, which is a nice library that helps to make auto layout more readable and enjoyable. https://github.com/robb/Cartography
There is no reason to update the width manually:
Place all the views with equal width in your view with no spacing in between each other
Add an equal width constraint to all of them
Add constraints with 0 width for spacing between sides and each other
Lower the priority of one or more of the equal width constraints just in case the width cannot be divided equally.
Then auto layout will handle everything for you.

Resources