How to get safe area frame of programmatically created ViewController? - ios

Note : Not created ViewController in storyboard
I created ViewController programmatically after giving support for safe guard now my problem is that how i can got his height?
I try following thing but no luck
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
print("\(height)")
print("\(self.view.frame.height)")
height = 812.0, self.view height = 812.0
Is there any other way?

A safeAreaLayoutGuide, which is iOS 11+, is meant for auto layout and to try to get a frame is close to trying to make orange juice from a lemon.
If what you want is to find out is what you can safely use for a frame, maybe try something like this:
Create a transparent view:
let mySafeView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mySafeView.backgroundColor = UIColor.clear
mySafeView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mySafeView)
// if needed, send this view to the back of the hierarchy
view.sendSubview(toBack: mySafeView)
}
Next, in your constraint-building code attach it to the true anchors:
// create generic layout guides
let layoutGuideTop = UILayoutGuide()
view.addLayoutGuide(layoutGuideTop)
let layoutGuideBottom = UILayoutGuide()
view.addLayoutGuide(layoutGuideBottom)
let margins = view.layoutMarginsGuide
view.addLayoutGuide(margins)
// get correct top/bottom margins based on iOS version
if #available(iOS 11, *) {
let guide = view.safeAreaLayoutGuide
layoutGuideTop.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0).isActive = true
layoutGuideBottom.bottomAnchor.constraintEqualToSystemSpacingBelow(guide.bottomAnchor, multiplier: 1.0).isActive = true
} else {
layoutGuideTop.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
layoutGuideBottom.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
mySafeView.topAnchor.constraint(equalTo: layoutGuideTop.topAnchor).isActive = true
mySafeView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mySafeView.bottomAnchor.constraint(equalTo: layoutGuideBottom.bottomAnchor).isActive = true
mySafeView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
Finally, get the frame size:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print(mySafeView.frame)
}
On an iPhone 6 plus running iOS 11.1 you get:
(0.0, 28.0, 414.0, 716.0)
On an iPhone 6 plus running iOS 9.0 you get:
(0.0, 20.0, 414.0, 716.0)

First off you can't do it in the viewDidLoad. You have to wait till viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myGlobalVars.sceneRect = view.frame
if #available(iOS 11.0, *) {
myGlobalVars.topSafeArea = view.safeAreaInsets.top
myGlobalVars.bottomSafeArea = view.safeAreaInsets.bottom
} else {
myGlobalVars.topSafeArea = topLayoutGuide.length
myGlobalVars.bottomSafeArea = bottomLayoutGuide.length
}
}

Related

How to Apply Animation while changing UIView size? + Swift 5

Im new to IOS development and I have two buttons in the UIView and when user select the option portrait or landscape, change the UIView re-size and change the background color as well and i need to add animation for that process.
As as ex:
User select portrait and then user can see red color UIVIew. after click the landscape option, animation should be started and it looks like, red color image come front and change the size (changing height and width) for landscape mode and go to previous position and change color to green. i have added small UIView animation on code and it is helpful to you identify the where should we start the animation and finish it. if someone know how to it properly, please, let me know and appreciate your help. please, refer below code
import UIKit
class ViewController: UIViewController {
let portraitWidth : CGFloat = 400
let portraitHeight : CGFloat = 500
let landscapeWidth : CGFloat = 700
let landscapeHeight : CGFloat = 400
var mainView: UIView!
var mainStackView: UIStackView!
let segment: UISegmentedControl = {
let segementControl = UISegmentedControl()
return segementControl
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.translatesAutoresizingMaskIntoConstraints = false
mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.alignment = .center
mainStackView.distribution = .equalCentering
self.view.addSubview(mainStackView)
self.segment.insertSegment(withTitle: "Portrait", at: 0, animated: false)
self.segment.insertSegment(withTitle: "Landscape", at: 1, animated: false)
self.segment.selectedSegmentIndex = 0
self.segment.addTarget(self, action: #selector(changeOrientation(_:)), for: .valueChanged)
self.segment.translatesAutoresizingMaskIntoConstraints = false
self.segment.selectedSegmentIndex = 0
mainStackView.addArrangedSubview(self.segment)
let safeAreaLayoutGuide = self.view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
self.mainStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
self.mainStackView.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor, constant: 0),
])
NSLayoutConstraint.activate([
self.segment.heightAnchor.constraint(equalToConstant: 35),
self.segment.widthAnchor.constraint(equalToConstant: 300)
])
mainView = UIView(frame: .zero)
mainView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubview(mainView)
mainView.backgroundColor = UIColor.red
NSLayoutConstraint.activate([
mainView.heightAnchor.constraint(equalToConstant: portraitHeight),
mainView.widthAnchor.constraint(equalToConstant: portraitWidth),
mainView.topAnchor.constraint(equalTo: segment.bottomAnchor, constant: 30)
])
}
#IBAction func changeOrientation(_ sender: UISegmentedControl) {
self.mainView.constraints.forEach{ (constraint) in
self.mainView.removeConstraint(constraint)
}
UIView.animate(withDuration: 1.0) {
if (sender.selectedSegmentIndex == 0) {
self.mainView.backgroundColor = UIColor.red
NSLayoutConstraint.activate([
self.mainView.heightAnchor.constraint(equalToConstant: self.portraitHeight),
self.mainView.widthAnchor.constraint(equalToConstant: self.portraitWidth),
self.mainView.topAnchor.constraint(equalTo: self.segment.bottomAnchor, constant: 30)
])
} else {
self.mainView.backgroundColor = UIColor.green
NSLayoutConstraint.activate([
self.mainView.heightAnchor.constraint(equalToConstant: self.landscapeHeight),
self.mainView.widthAnchor.constraint(equalToConstant: self.landscapeWidth),
self.mainView.topAnchor.constraint(equalTo: self.segment.bottomAnchor, constant: 30)
])
}
}
}
}
updated logic
#IBAction func changeOrientation(_ sender: UISegmentedControl) {
UIView.animate(withDuration: 4.0) {
if (sender.selectedSegmentIndex == 0) {
self.mainView.backgroundColor = UIColor.red
self.widthConstraint.constant = self.portraitWidth
self.heightConstraint.constant = self.portraitWidth
} else {
self.mainView.backgroundColor = UIColor.green
self.widthConstraint.constant = self.landscapeWidth
self.heightConstraint.constant = self.landscapeHeight
}
self.mainView.layoutIfNeeded()
} }
It's possible, the basic problem is:
• It's not possible to animate an actual change between one constraint and another.
To change size or shape you simply animate the length of a constraint.
While you are learning I would truly urge you to simply animate the constraints.
So, don't try to "change" constraints, simply animate the length of one or the other.
yourConstraint.constant = 99 // it's that easy
I also truly urge you to just lay it out on storyboard.
You must master that first!
Also there is no reason at all for the stack view, get rid of it for now.
Just have an outlet
#IBOutlet var yourConstraint: NSLayoutConstraint!
and animate
UIView.animateWithDuration(4.0) {
self.yourConstraint.constant = 666
self.view.layoutIfNeeded()
}
Be sure to search in Stack Overflow for "iOS animate constraints" as there is a great deal to learn.
Do this on storyboard, it will take 20 seconds tops for such a simple thing. Once you have mastered storyboard, only then move on to code if there is some reason to do so
Forget the stack view
Simply animate the constant of the constraint(s) to change the size, shape or anything you like.
Be sure to google for the many great articles "animating constraints in iOS" both here on SO and other sites!

Not able to lay out Subviews on a custom UIView in Swift

So I have created this custom container view which I am laying out using autolayout constraint.
func configureSegmentContainerView() {
segmentContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
segmentContainerView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8).isActive = true
segmentContainerView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8).isActive = true
segmentContainerView.heightAnchor.constraint(equalToConstant: 3).isActive = true
}
In the view controller, the viewDidLoad() is this:
setupDataSource()
segmentContainerView = ATCStorySegmentsView()
view.addSubview(segmentContainerView)
configureSegmentContainerView()
segmentContainerView.translatesAutoresizingMaskIntoConstraints = false
segmentContainerView.numberOfSegments = friendStory.count
Now once the data source is setup and I have the friendStory count, I am assigning it to segmentContainerView.numberofSegments
In segmentContainerview class this is what is happening:
var numberOfSegments: Int? {
didSet {
addSegments()
}
}
In addSegments(), I am adding UIViews depending upon the numberOfSegments this is the code for that:
private func addSegments() {
guard let numberOfSegments = numberOfSegments else { return }
layoutIfNeeded()
setNeedsLayout()
for i in 0..<numberOfSegments {
let segment = Segment()
addSubview(segment.bottomSegment)
addSubview(segment.topSegment)
configureSegmentFrame(index: i, segmentView: segment)
segmentsArray.append(segment)
}
}
private func configureSegmentFrame(index: Int, segmentView: Segment) {
guard let numberOfSegments = numberOfSegments else { return }
let widthOfSegment : CGFloat = (self.frame.width - (padding * CGFloat(numberOfSegments - 1))) / CGFloat(numberOfSegments)
let i = CGFloat(index)
let segmentFrame = CGRect(x: i * (widthOfSegment + padding), y: 0, width: widthOfSegment, height: self.frame.height)
segmentView.bottomSegment.frame = segmentFrame
segmentView.topSegment.frame = segmentFrame
segmentView.topSegment.frame.size.width = 0
}
**Question and Issue: ** Instead of getting 4 UIViews, I am getting 3, but the third one is not correctly placed and is going outside the parent container. How can I get these uiviews aligned correctly. I am guessing there is some issue with where setNeedsLayout() and layoutIfNeeded() needs to be called. Please help.
Segment is a struct with two properties - bottomSegment and topSegment. Both being UIView
You can see how just three UIView segments appear. I needs to 4 (numberOfSegments = 4) of these. Also I am giving the parent container constant of 8 and -8 for right and leftAnchor. so all 4 segments need to be placed within this view. As you can see in the picture above the last segment is going outside of the parent container.
Try to call addSegments at onViewDidAppear. If it works, it means that in your code the view does not still have the correct frame.
For this to work you need to adapt the frames of the views, when the view controller's view changed:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let numberOfSegments = numberOfSegments else { return }
for i in 0..<numberOfSegments {
configureSegmentFrame(index: i, segmentView: segment)
}
}
You will probably have to do it cleaner, but it should work.
The problem is that
let widthOfSegment : CGFloat = (self.frame.width ...
is being called too soon. You cannot do anything in that depends upon self having its frame until after it has its real frame.
For a UIView, that moment is after layoutSubviews has been called for the first time.

IOS 11 navigation item with user interaction enabled not working

Hi i have navigation item in there is a view which contains two labels i have added userInteractionEnabled for the view inside the navigation element (IBoutleted as navigationView)
navigationView.isUserInteractionEnabled = true
mainTitleClicked = UITapGestureRecognizer(target: self, action: #selector(mainTitleTapped))
self.navigationView.addGestureRecognizer(mainTitleClicked)
This was working in IOS 10 but when i run the same code in xcode 9 ios 11 UI is messed up and gesture is not getting recognised
IOS 10 Version
IOS 11 version
What should i change to make it work on ios 11
Thanks for the help
I Solved it By adding the following code
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationItem.largeTitleDisplayMode = .automatic
var width = navigationView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width - 15.0
let height = navigationView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).height
let screenSize: CGRect = UIScreen.main.bounds
let windowWidth = screenSize.width
width = windowWidth * 0.55
let widthConstraint = navigationView.widthAnchor.constraint(equalToConstant: width)
let heightConstraint = navigationView.heightAnchor.constraint(equalToConstant: height)
heightConstraint.isActive = true
widthConstraint.isActive = true
}
Making height and width constraint active was a answer given in another question i currently don't have the link to it will post the link in the edit (Currently i am unable to find that question)
I have this same problem and solved with this code too:
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationItem.largeTitleDisplayMode = .automatic
var width = popUserChatView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width - 15.0
let height = popUserChatView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).height
let screenSize: CGRect = UIScreen.main.bounds
let windowWidth = screenSize.width
width = windowWidth * 0.55
let widthConstraint = popUserChatView.widthAnchor.constraint(equalToConstant: width)
let heightConstraint = popUserChatView.heightAnchor.constraint(equalToConstant: height)
heightConstraint.isActive = true
widthConstraint.isActive = true
}
calling in ViewDidLoad()

Cannot fix Auto Layout animation while rotation event

Don't be afraid of the huge code that will follow here. You can copy and paste the code snippet into a new single view application to see how it behaves. The problem sits somewhere inside the completion block of the animation executed alongside the rotation animation.
import UIKit
let sizeConstant: CGFloat = 60
class ViewController: UIViewController {
let topView = UIView()
let backgroundView = UIView()
let stackView = UIStackView()
let lLayoutGuide = UILayoutGuide()
let bLayoutGuide = UILayoutGuide()
var bottomConstraints = [NSLayoutConstraint]()
var leftConstraints = [NSLayoutConstraint]()
var bLayoutHeightConstraint: NSLayoutConstraint!
var lLayoutWidthConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
print(UIScreen.main.bounds)
// self.view.layer.masksToBounds = true
let views = [
UIButton(type: .infoDark),
UIButton(type: .contactAdd),
UIButton(type: .detailDisclosure)
]
views.forEach(self.stackView.addArrangedSubview)
self.backgroundView.backgroundColor = UIColor.red
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.backgroundView)
self.topView.backgroundColor = UIColor.green
self.topView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.topView)
self.stackView.axis = isPortrait() ? .horizontal : .vertical
self.stackView.distribution = .fillEqually
self.stackView.translatesAutoresizingMaskIntoConstraints = false
self.backgroundView.addSubview(self.stackView)
self.topView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive = true
self.topView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.topView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
self.topView.heightAnchor.constraint(equalToConstant: 46).isActive = true
self.view.addLayoutGuide(self.lLayoutGuide)
self.view.addLayoutGuide(self.bLayoutGuide)
self.bLayoutGuide.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.bLayoutGuide.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.bLayoutGuide.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
self.bLayoutHeightConstraint = self.bLayoutGuide.heightAnchor.constraint(equalToConstant: isPortrait() ? sizeConstant : 0)
self.bLayoutHeightConstraint.isActive = true
self.lLayoutGuide.topAnchor.constraint(equalTo: self.topView.bottomAnchor).isActive = true
self.lLayoutGuide.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.lLayoutGuide.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.lLayoutWidthConstraint = self.lLayoutGuide.widthAnchor.constraint(equalToConstant: isPortrait() ? 0 : sizeConstant)
self.lLayoutWidthConstraint.isActive = true
self.stackView.topAnchor.constraint(equalTo: self.backgroundView.topAnchor).isActive = true
self.stackView.bottomAnchor.constraint(equalTo: self.backgroundView.bottomAnchor).isActive = true
self.stackView.leadingAnchor.constraint(equalTo: self.backgroundView.leadingAnchor).isActive = true
self.stackView.trailingAnchor.constraint(equalTo: self.backgroundView.trailingAnchor).isActive = true
self.bottomConstraints = [
self.backgroundView.topAnchor.constraint(equalTo: self.bLayoutGuide.topAnchor),
self.backgroundView.leadingAnchor.constraint(equalTo: self.bLayoutGuide.leadingAnchor),
self.backgroundView.trailingAnchor.constraint(equalTo: self.bLayoutGuide.trailingAnchor),
self.backgroundView.heightAnchor.constraint(equalToConstant: sizeConstant)
]
self.leftConstraints = [
self.backgroundView.topAnchor.constraint(equalTo: self.lLayoutGuide.topAnchor),
self.backgroundView.bottomAnchor.constraint(equalTo: self.lLayoutGuide.bottomAnchor),
self.backgroundView.trailingAnchor.constraint(equalTo: self.lLayoutGuide.trailingAnchor),
self.backgroundView.widthAnchor.constraint(equalToConstant: sizeConstant)
]
if isPortrait() {
NSLayoutConstraint.activate(self.bottomConstraints)
} else {
NSLayoutConstraint.activate(self.leftConstraints)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let willBePortrait = size.width < size.height
coordinator.animate(alongsideTransition: {
context in
let halfDuration = context.transitionDuration / 2.0
UIView.animate(withDuration: halfDuration, delay: 0, options: .overrideInheritedDuration, animations: {
self.bLayoutHeightConstraint.constant = 0
self.lLayoutWidthConstraint.constant = 0
self.view.layoutIfNeeded()
}, completion: {
_ in
// HERE IS THE ISSUE!
// Putting this inside `performWithoutAnimation` did not helped
if willBePortrait {
self.stackView.axis = .horizontal
NSLayoutConstraint.deactivate(self.leftConstraints)
NSLayoutConstraint.activate(self.bottomConstraints)
} else {
self.stackView.axis = .vertical
NSLayoutConstraint.deactivate(self.bottomConstraints)
NSLayoutConstraint.activate(self.leftConstraints)
}
self.view.layoutIfNeeded()
UIView.animate(withDuration: halfDuration) {
if willBePortrait {
self.bLayoutHeightConstraint.constant = sizeConstant
} else {
self.lLayoutWidthConstraint.constant = sizeConstant
}
self.view.layoutIfNeeded()
}
})
})
super.viewWillTransition(to: size, with: coordinator)
}
func isPortrait() -> Bool {
let size = UIScreen.main.bounds.size
return size.width < size.height
}
}
Here are a few screenshots of the issue I'm unable to solve. Look closely at the corners:
I'd assume that after reactivating different constraint array and force recalculation, the view would immediately snap to the layout guide, but as shown, it doesn't. Furthermore I don't understand why the red view is not in sync with the stack view, even if the stackview should always follow it's superview, which here is the red view.
PS: The best way to test it is the iPhone X Plus simulator.
Use Size Classes
An entirely different approach to smoothly animate the toolbar animation is to leverage on the autoLayout size classes, specifically hR (height Regular) and hC (height Compact), and create different constraints for each.
↻ replay animation
A further improvement is to actually use two distinct toolbars, one for the vertical display, and one for the horizontal one. This is not by any mean a requirement, but it solves the resizing of the toolbar itself (†).
A final refinement is to implement these changes in Interface Builder, yielding exactly 0 lines of code, which of course is not mandatory either.
↻ replay animation
Zero Lines of Code
None of the proposed solutions tinker with UIViewControllerTransitionCoordinator which not only greatly simplify the source code development and maintenance, it also doesn't need to rely on hardcoded values or supporting utilities. You also get a preview in Interface Builder. And once finalized in IB, you can still convert the logic to runtime programming if it is an absolute requirement.
Notice that the UIStackView is embedded in the toolbar, and thus follows the animation. You can control the amount of swing of the toolbars out of sight by a constant ; I picked 1024 so that they move quickly out of the screen, and only reappear at the end of the transition.
(†) Further leveraging on Interface Builder and size classes, you may still use a single toolbar, but if you do so it will resize during the transition. Again, the UIStackView is embedded, and its orientation is, too, size classes dependent, and the OS handles all the animation without the need to create a coordinator:
► Find this solution on GitHub and additional details on Swift Recipes.
I'd assume that after reactivating different constraint array and force recalculation, the view would immediately snap to the layout guide, but as shown, it doesn't.
This isn't happening because you are activating/deactivating your constraints inside of the coordinator.animate(alongsideTransition:completion:) method. When you use this method, everything inside of the animation block will be animated along with your view controller's transition, so there will be no immediate snaps to the layout guide. If you wanted the red view and green view to immediately snap to their new positions before the rotation animation, then animate to fill their desired positions as the view controller is animating, then you could do something like this:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let willBePortrait = size.width < size.height
self.bLayoutHeightConstraint.constant = 0
self.lLayoutWidthConstraint.constant = 0
if willBePortrait {
self.stackView.axis = .horizontal
NSLayoutConstraint.deactivate(self.leftConstraints)
NSLayoutConstraint.activate(self.bottomConstraints)
} else {
self.stackView.axis = .vertical
NSLayoutConstraint.deactivate(self.bottomConstraints)
NSLayoutConstraint.activate(self.leftConstraints)
}
self.view.layoutIfNeeded()
coordinator.animate(alongsideTransition: { context in
let halfDuration = context.transitionDuration / 2
UIView.animate(withDuration: halfDuration, delay: halfDuration, animations: {
if willBePortrait {
self.bLayoutHeightConstraint.constant = sizeConstant
} else {
self.lLayoutWidthConstraint.constant = sizeConstant
}
self.view.layoutIfNeeded()
})
})
super.viewWillTransition(to: size, with: coordinator)
}
EDIT: If you want to animate the red view out while transitioning, and then animate the red view back in at the end, then that's a good time to use the coordinator, as the animation out can happen in the animation block and you can split the out- and in- blocks inside of it:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let willBePortrait = size.width < size.height
if willBePortrait {
self.stackView.axis = .horizontal
NSLayoutConstraint.deactivate(self.leftConstraints)
NSLayoutConstraint.activate(self.bottomConstraints)
} else {
self.stackView.axis = .vertical
NSLayoutConstraint.deactivate(self.bottomConstraints)
NSLayoutConstraint.activate(self.leftConstraints)
}
self.view.layoutIfNeeded()
coordinator.animate(alongsideTransition: { context in
let halfDuration = context.transitionDuration / 2
UIView.animate(withDuration: halfDuration, animations: {
if willBePortrait {
self.lLayoutWidthConstraint.constant = 0
} else {
self.bLayoutHeightConstraint.constant = 0
}
})
UIView.animate(withDuration: halfDuration, delay: halfDuration, animations: {
if willBePortrait {
self.bLayoutHeightConstraint.constant = sizeConstant
} else {
self.lLayoutWidthConstraint.constant = sizeConstant
}
self.view.layoutIfNeeded()
})
})
super.viewWillTransition(to: size, with: coordinator)
}

AutoLayout incorrectly rendering subview's position

I am in the process of learning auto layout and I use snapkit. I written some code but working differently than expected. I write code leftMargin but it is working as if it's a rightMargin. You can see on photo. what is wrong on my code?
My Code
let container = View()
container.backgroundColor=UIColor.greenColor()
let v1 = View()
v1.backgroundColor=UIColor.blackColor()
self.view.addSubview(container);
container.addSubview(v1)
let padding2 : UIEdgeInsets = UIEdgeInsetsMake(20,20,20,20)
container.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.view).offset(padding2.top)
make.bottom.equalTo(self.view).offset(-padding2.bottom)
// make.left.equalTo(self.view).inset(padding2.left)
make.left.equalTo(self.view).offset(padding2.left)
make.right.equalTo(self.view).offset(-padding2.right)
//make.width.equalTo(self.view.bounds.width-90)
/*
make.top.equalTo(self.view).offset(20)
make.left.equalTo(self.view).offset(20)
make.bottom.equalTo(self.view).offset(-20)
make.right.equalTo(self.view).offset(-20)
*/
}
let padding : UIEdgeInsets = UIEdgeInsetsMake(50, 50, 15, 10)
v1.snp_makeConstraints { (make) -> Void in
make.topMargin.equalTo(container).offset(padding.top);
make.leftMargin.equalTo(container).offset(padding.left);
make.width.equalTo(100);
make.height.equalTo(100);
}
replace
make.topMargin.equalTo(container).offset(padding.top);
make.leftMargin.equalTo(container).offset(padding.left);
with
make.top.equalTo(container).offset(padding.top);
make.left.equalTo(container).offset(padding.left);
The difference of left and leftMargin? Check this: https://stackoverflow.com/a/28692783/3331750
I've never used snapkit but in regular auto layout you can achieve what you want in the following way.
let container = UIView()
container.backgroundColor=UIColor.greenColor()
let v1 = UIView()
v1.backgroundColor=UIColor.blackColor()
self.view.addSubview(container)
container.addSubview(v1)
// enable the views to be resized by auto layout
container.translatesAutoresizingMaskIntoConstraints = false
v1.translatesAutoresizingMaskIntoConstraints = false
// pin the container to all edges of the main view
container.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor).active = true
container.bottomAnchor.constraintEqualToAnchor(bottomLayoutGuide.topAnchor).active = true
container.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
container.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
// anchor the v1 subview to the
v1.topAnchor.constraintEqualToAnchor(container.topAnchor, constant: 20).active = true
v1.leadingAnchor.constraintEqualToAnchor(container.leadingAnchor, constant: 20).active = true
// set a fixed height and width
v1.heightAnchor.constraintEqualToConstant(100).active = true
v1.widthAnchor.constraintEqualToConstant(100).active = true
and for proportional height and width replace the final two lines of code with:
v1.widthAnchor.constraintEqualToAnchor(view.widthAnchor, multiplier: 0.25).active = true
v1.heightAnchor.constraintEqualToAnchor(view.widthAnchor, multiplier: 0.25).active = true

Resources