Search code examples
swiftautolayoutsnapkit

Updating variable in view constraint (SnapKit)


View is initialized with following constraints


  View.snp.makeConstraints { (para) in
       View.topConstraint = para.top.equalTo(parentview.snp.top).constraint
       View.LeadingConstraint = para.leading.equalTo(parentview.snp.leading).constraint
       View.TrailingConstraint = para.trailing.equalTo(parentview.snp.trailing).constraint
       View.BottomConstraint =para.bottom.equalTo(parentview.snp.bottom).offset(-getheight).constraint
        
  }

where getheight = parentview.frame.size.height/2 ;

when parentview changes its dimensions.View doesnt update its height as constraints are not called again. any way to update or recall its constraints other the remakingConstraint which is not feasible at large scale. Have tried:

View.updateConstraints()
View.setNeedsUpdateConstraints()
View.setNeedsLayout()

I need reference to each constraints because

if View.bottomTouch {
      View.bottomConstraint.update(offset: View. BottomConstraint.layoutConstraints[0].constant + CurrentPoint - PreviousPoint)
}

Solution

  • Is there a reason you don't want to use 50% of the parent view height?

        View.snp.makeConstraints { (para) in
            para.top.equalTo(parentview.snp.top)
            para.leading.equalTo(parentview.snp.leading)
            para.trailing.equalTo(parentview.snp.trailing)
    
            // 50% of the parent view height
            para.height.equalTo(parentview.snp.height).multipliedBy(0.5)
    
            // instead of this
            //para.bottom.equalTo(parentview.snp.bottom).offset(-getheight)
        }
    

    Edit - after comments...

    Keeping a reference to a constraint for the purposes of dragging a view is a very different question from "Keep the child view at 50% of the height of the parent view."

    Give this a try...

    It will create a cyan "parentView" with a blue "childView" (subview). Dragging the blue view (Pan Gesture) will drag its bottom up / down. Tapping anywhere (Tap Gesture) will toggle the insets on the frame of the parentView between 20 and 60.

    When the parentView frame changes - either from the tap or, for example, on device rotation - the "childView" bottom will be reset to 50% of the height of the "parentView":

    class ViewController: UIViewController {
    
        let parentView = UIView()
        let childView = UIView()
        
        // childView bottom constraint
        var bc: Constraint!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            parentView.backgroundColor = .cyan
            childView.backgroundColor = .blue
            
            parentView.addSubview(childView)
            view.addSubview(parentView)
            
            parentView.snp.makeConstraints { para in
                para.top.leading.trailing.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20.0)
            }
            
            // childView's bottom constraint offset will be set in viewDidLayoutSubviews()
            childView.snp.makeConstraints { para in
                para.top.leading.trailing.equalToSuperview()
                bc = para.bottom.equalToSuperview().constraint
            }
            
            let p = UIPanGestureRecognizer(target: self, action: #selector(panHandler(_:)))
            childView.addGestureRecognizer(p)
            
            let t = UITapGestureRecognizer(target: self, action: #selector(tapHandler(_:)))
            view.addGestureRecognizer(t)
        }
    
        @objc func tapHandler(_ g: UITapGestureRecognizer) -> Void {
            
            // on tap, toggle parentView inset
            //  between 20 and 60
            // this will trigger viewDidLayoutSubviews(), where the childView bottom
            //  constraint will be reset to 50% of the parentView height
            var i: CGFloat = 60.0
            if parentView.frame.origin.x > 20 {
                i = 20.0
            }
            parentView.snp.updateConstraints { para in
                para.top.leading.trailing.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(i)
            }
            
        }
    
        @objc func panHandler(_ g: UIPanGestureRecognizer) -> Void {
            
            let translation = g.translation(in: g.view)
    
            // update bottom constraint constant
            bc.layoutConstraints[0].constant += translation.y
            
            // reset gesture translation
            g.setTranslation(CGPoint.zero, in: self.view)
    
        }
        
        var parentViewHeight: CGFloat = 0.0
        
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            
            // reset childView's bottom constraint
            //  to 50% of its superView's height
            //  ONLY if parentView frame height has changed
            if parentView.frame.height != parentViewHeight {
                parentViewHeight = parentView.frame.height
                bc.layoutConstraints[0].constant = -parentViewHeight * 0.5
            }
            
        }
        
    }