AutoLayout not updating views programmatically - Swift - ios

I have the following code where I'm creating and laying out everything in code. What I'm trying to do is modify the myTopContainer when a button is tapped but I can not make the width changed, only the height gets modified.
Any idea why?
func setupAutoLayout(){
NSLayoutConstraint.activate([
// My Container
myTopContainer.bottomAnchor.constraint(equalTo: myButton.topAnchor, constant: -5),
myTopContainer.heightAnchor.constraint(equalToConstant: 50),
myTopContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
myTopContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
// My Button
myButton.widthAnchor.constraint(equalToConstant: 80),
myButton.heightAnchor.constraint(equalToConstant: 40),
myButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -15),
myButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -400)
])
}
#IBAction func relayoutViews(_ sender: Any) {
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
self.myTopContainer.heightAnchor.constraint(equalToConstant: 50).isActive = false
self.myTopContainer.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 15).isActive = false
// this is working, it changes the height
self.myTopContainer.heightAnchor.constraint(equalToConstant: 10).isActive = true
// this is not doing anything
self.myTopContainer.leadingAnchor.constraint(equalTo: self.myButton.trailingAnchor).isActive = true
self.view.layoutIfNeeded()
})
}

A line like this
self.myTopContainer.heightAnchor.constraint(equalToConstant: 50).isActive = false
Creates a new constraint every time. It doesn't modify the previously created constraint.
When you setupAutolayout you should save a reference to the constraints you want to later modify before actually calling NSLayoutConstraint.activate() on them.
Later you can set isActive to false on them.
For example:
var someConstraint: NSLayoutConstraint?
func setupAutoLayout(){
let aConstraint = myTopContainer.bottomAnchor.constraint(equalTo: myButton.topAnchor, constant: -5)
someConstraint = aConstraint
NSLayoutConstraint.activate([
// My Container
aConstraint
])
}
func editConstraint() {
self.someConstraint?.isActive = false
}
Anyway be careful on logs when you edit constraints, it's probable that you could break something while editing. If you see those logs of broken constraint than it means that something is still wrong (even if it visually looks good, it could crash or not work properly in some other situation)

Related

constraints not working programmatically swift

func textFields() {
let nameField = MDCFilledTextField()
view.addSubview(nameField)
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
nameField.placeholder = "Name"
}
I am using google material design and create a textfield.For this purpose and i have used this code i want to space from both sides left and right but it works only one side left or right i want to space from both side.
You are trying to set the MDCFilledTextField position in a wrong way. The following line tells your view to use a static predefined frame size:
let estimatedFrame = CGRect(x: 10, y: 200, width: UIScreen.main.bounds.width-20, height: 50)
let nameField = MDCFilledTextField(frame: estimatedFrame)
But further down with the following rows you tell your view to use autolayout:
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
So now you have to decide which one would you like to use, the static frame or the autolayout. If you decide to go with the autolayout you have to improve your code in order to remove the error you get. First you need to set the translatesAutoresizingMaskIntoConstraints to true instead of false.
nameField.translatesAutoresizingMaskIntoConstraints = false
This line will tell that you want to use the autolayout for nameField instead of a static frame. Further you need to add your view to the superview first, otherwise you can't define your constraints(therefore the error you have). So your code becomes:
func textFields() {
let nameField = MDCFilledTextField()
view.addSubview(nameField)
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
nameField.placeholder = "Name"
}
Add the nameField as a subview of the view before activating the constraint and remove of using frames.
To add padding use for example centerXAnchor and widthAnchor + multiplier
func textFields() {
let nameField = MDCFilledTextField()
nameField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(nameField)
NSLayoutConstraint.activate([
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
nameField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
nameField.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9)
])
nameField.placeholder = "Name"
nameField.label.text = "Name"
nameField.setFloatingLabelColor(.lightGray, for: MDCTextControlState.editing)
}
or add constants to the leftAnchor and rightAnchor.
func textFields() {
let nameField = MDCFilledTextField()
nameField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(nameField)
NSLayoutConstraint.activate([
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
nameField.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10),
nameField.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10)
])
nameField.placeholder = "Name"
nameField.label.text = "Name"
nameField.setFloatingLabelColor(.lightGray, for: MDCTextControlState.editing)
}

Revert NSLayoutConstraint of UIView after changing the constraint

Currently, I am expanding a custom view inside a scrollview to
fullscreen after the user taps on it. But I want the View to come back
to the original position once the user taps on it again. Note:- I have
set the original custom view position inside the scrollview on Xib
visually, I haven't added the code programmatically to add views in
scroll view.
Here is my code to expand:-
let frameAnimator = UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
self.playerDescriptionView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true
self.playerDescriptionView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true
self.playerDescriptionView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
self.playerDescriptionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
self.view.layoutIfNeeded()
}
frameAnimator.startAnimation()
So, how can I set the constraint back to the original position while minimizing the view, because I have not set the view constraint programmatically?

TableView not appearing after setting the constraints programmatically

I have a UITableView in my storyboard. I am trying to setup constraints for it in my view controller. After running the application it's not showing up at all. it's only working when i don't run the constraints. here are my constraints.
func tableviewsConstraints(){
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.layer.cornerRadius = 10
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
homeTableView.heightAnchor.constraint(equalToConstant: homeTableView.rowHeight * 3).isActive = true
self.view.addSubview(homeTableView)
}
override func viewDidLoad() {
super.viewDidLoad()
tableviewsConstraints()
Add the homeTableView as the view's subview before adding the constraints to it. And use a constant value when setting the height of tableView, i.e.
func tableviewsConstraints(){
self.view.addSubview(homeTableView) //here....
homeTableView.layer.cornerRadius = 10
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
let rowHeight: CGFloat = 100.0 //here....
homeTableView.heightAnchor.constraint(equalToConstant: rowHeight * 3).isActive = true
}
Change the rowHeight value as per your requirement.
Don't you have the outlet of the table view if you have it in the storyboard? Then why you need to subview? If you're creating table view programmatically then follow this.

Set constraint on label from below collection view programmatically

Set constraint lbl_Title from bottom to collectionView.
On setting the bottom constraint 60, the label goes below the collection view, after setting it to -60 then it's adjusted to location.
How to set constraints based on collection?
func setCollectionViewConstraints() -> Void {
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 10).isActive = true
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
collectionView.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
}
func setRecentJobLabelConstraints() -> Void {
lbl_Title.translatesAutoresizingMaskIntoConstraints = false
lbl_Title.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -60).isActive = true
lbl_Title.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
lbl_Title.heightAnchor.constraint(greaterThanOrEqualToConstant: 20).isActive = true
}
Here the issue is fixed if the constraint is set to -60, I think it's the wrong way.
Setting -60 is the right way. The coordinate system for CocoaTouch is a bit strange because it's (0,0) is in the top-left corner of the device, compared to the coordinated in Cocoa which starts from bottom-left. You'll get used to this once you do more auto-layout programmatically.
Note: Also, you need to give negative values when trying to constraint sub-views to super-views from right.
Different Approach: Another approach would be to constraint the super-view to the sub-view this way it's more readable and self-explanatory. Constraint the bottomAnchor of super-view to sub-view's bottomAnchor with a padding of 60 points.
bottomAnchor.constraint(equalTo: lbl_Title.bottomAnchor, constant: 60).isActive = true
It is not a wrong way , calling the constant while using bottom & trailing constraints should be with a minus value , you can use the below extension i created rather than repeating the same autolayout lines over & over
// MARK: - Anchors Method
extension UIView {
func anchors (top:NSLayoutYAxisAnchor? , leading:NSLayoutXAxisAnchor? , bottom : NSLayoutYAxisAnchor? , trailing: NSLayoutXAxisAnchor? , padding : UIEdgeInsets = .zero){
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top , constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading , constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom , constant: -padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing , constant: -padding.right).isActive = true
}
}
}
and call it like below this :
YourUIView.anchors(top: View.topAnchor , leading: View.leadingAnchor , bottom: View.bottomAnchor , trailing: View.trailingAnchor , padding: .init(top: 10, left: 10, bottom: 10, right: 10))
A quick advice , there is no need to assign a void as a return since the function is not returning something .

Swift - Hero animation to dismiss View

I am having trouble using the Hero Library to dismiss my ViewController with custom animation.
In the end I would like to have pretty much the exact same animation as in this video:
preferred dismiss animation
So far my dismiss animation looks like this:
my dismiss animation so far
I am having 3 major problems which I can not figure out:
1.
When presenting/dismissing my ViewController there seems to be this white background behind my 2nd ViewController but I would like to just cover my first ViewController with the 2nd without any white views.
2.
My image disappears after the user starts swiping down behind my views when instead it should not change its position (preferred dismiss animation) until the view actually dismisses. Same things goes for the add-Button in the bottom right corner.
3. Like in the preferred dismiss animation my the backgroundView(lightGray View in the 2nd video) should dismiss a bit fast then the other subviews. I tried using the cascade-modifier but couldn't get that effect.
This is my 2nd ViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.wishlistBackgroundView.hero.isEnabled = true
self.wishlistBackgroundView.heroID = "wishlistView"
self.wishlistBackgroundView.hero.modifiers = [.fade, .translate(CGPoint(x: 0, y: 800), z: 20)]
// adding panGestureRecognizer
panGR = UIPanGestureRecognizer(target: self,
action: #selector(handlePan(gestureRecognizer:)))
view.addGestureRecognizer(panGR)
self.wishlistLabel.text = wishList.name
self.wishlistImage.image = wishList.image
self.theTableView.wishList = wishList.wishData
self.theTableView.tableView.reloadData()
view.addSubview(wishlistBackgroundView)
view.addSubview(dismissWishlistViewButton)
view.addSubview(menueButton)
wishlistBackgroundView.addSubview(wishlistView)
wishlistBackgroundView.addSubview(wishlistLabel)
wishlistBackgroundView.addSubview(wishlistImage)
wishlistView.addSubview(theTableView.tableView)
wishlistView.addSubview(addWishButton)
NSLayoutConstraint.activate([
// constrain wishlistView
wishlistBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
wishlistBackgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
wishlistBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
wishlistBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
wishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 160.0),
wishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
wishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0),
wishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0),
// constrain wishTableView
theTableView.view.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: 60.0),
theTableView.view.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: 0),
theTableView.view.leadingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
theTableView.view.trailingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
// constrain dismissButton
dismissWishlistViewButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
dismissWishlistViewButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 23.0),
// constrain menueButton
menueButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
menueButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -25.0),
// constrain wishlistImage
wishlistImage.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: -70),
wishlistImage.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor, constant: 30),
wishlistImage.widthAnchor.constraint(equalToConstant: 90),
wishlistImage.heightAnchor.constraint(equalToConstant: 90),
//constrain wishlistlabel
wishlistLabel.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: -47),
wishlistLabel.leadingAnchor.constraint(equalTo: wishlistImage.leadingAnchor, constant: 100),
addWishButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
addWishButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40),
])
// set DeleteWishDelegate protocol for the table
theTableView.deleteWishDelegate = self
}
// define a small helper function to add two CGPoints
func addCGPoints (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
// handle swqipe down gesture
#objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
// calculate the progress based on how far the user moved
let translation = panGR.translation(in: nil)
let progress = translation.y / 2 / view.bounds.height
switch panGR.state {
case .began:
// begin the transition as normal
dismiss(animated: true, completion: nil)
case .changed:
Hero.shared.update(progress)
// update views' position based on the translation
let viewPosition = CGPoint(x: wishlistBackgroundView.center.x, y: translation.y + wishlistBackgroundView.center.y)
Hero.shared.apply(modifiers: [.position(viewPosition)], to: self.wishlistBackgroundView)
default:
// finish or cancel the transition based on the progress and user's touch velocity
if progress + panGR.velocity(in: nil).y / view.bounds.height > 0.3 {
Hero.shared.finish()
} else {
Hero.shared.cancel()
}
}
}
I couldn't find any good tutorials on this nor anything on these topics on git. If anyone knows even one answer to any of the problems I am more than happy.
Technically these are 3 questions but they are quite related. If this is against SO rules, I am happy to ask them separately.

Resources