Reputation: 3
I have a custom view , CustomLayout (blue, custom UIView), and this view contains 3 subviews vertical aligned by using constraints (Layout Anchors), each view are aligned following this order:
I want when I clicked on the button (yellow), the height size of SlideLayout (red) increase if open or decrease if close by using animation. And other views must change position during animation and the parent view (CustomLayout) must increase/decrease his height size (animation) if SlideLayout increase/decrease.
What method is called when I use this method:
UIView.animate(withDuration, delay, options, animations, completion)
I override layoutIfNeeded() method by adding a simple print but it does not call every time during animation
I try this but It doesn’t work as I expect. How I can fix that thank you.
Codes:
class CustomLayout: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
onLayout()
}
public func onLayout() {
print("\(frame.size.height)")
let MARGIN: CGFloat = 10
for i in 0 ..< subviews.count {
let child = subviews[i]
if i == 0 { // slide layout
child.translatesAutoresizingMaskIntoConstraints = false
child.topAnchor.constraint(equalTo: topAnchor, constant: MARGIN).isActive = true
child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 2)).isActive = true
let enchorHeight = child.heightAnchor.constraint(equalToConstant: child.frame.size.height);
enchorHeight.isActive = true
(subviews[0] as! SlideDownLayout).enchorHeight = enchorHeight
}
else if i == 1 { // button
child.translatesAutoresizingMaskIntoConstraints = false
child.topAnchor.constraint(equalTo: subviews[0].bottomAnchor, constant: MARGIN).isActive = true
child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
child.widthAnchor.constraint(equalToConstant: child.frame.size.width).isActive = true
child.heightAnchor.constraint(equalToConstant: child.frame.size.height).isActive = true
}
else if i == 2 { // uiview
child.translatesAutoresizingMaskIntoConstraints = false
child.topAnchor.constraint(equalTo: subviews[1].bottomAnchor, constant: MARGIN).isActive = true
child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 4)).isActive = true
child.heightAnchor.constraint(equalToConstant: 300).isActive = true
bottomAnchor.constraint(equalTo: child.bottomAnchor, constant: MARGIN).isActive = true
}
}
}
}
class SlideDownLayout: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private var HEIGHT: CGFloat = 0
private var isClosed: Bool = true
private func setup() {
HEIGHT = frame.height
frame.size.height = 0
}
public func slideAnimation(view: UIView) {
print("\(HEIGHT)")
isClosed = !isClosed
self.enchorHeight!.constant = self.isClosed ? 0 : self.HEIGHT
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut, animations: {
self.superview?.layoutIfNeeded()
view.layoutIfNeeded()
}, completion: nil)
}
override func layoutIfNeeded() {
print("...")
super.layoutIfNeeded()
}
var enchorHeight: NSLayoutConstraint? = nil
}
class ViewController: UIViewController {
@IBOutlet weak var customLayout: CustomLayout!
@IBOutlet weak var slideDownLayout: SlideDownLayout!
override func viewDidLoad() {
super.viewDidLoad()
customLayout.translatesAutoresizingMaskIntoConstraints = false
customLayout.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
customLayout.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
customLayout.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
customLayout.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
override var prefersStatusBarHidden: Bool {
return true
}
@IBAction
func buttonListener(_ sender: Any) {
slideDownLayout.slideAnimation(view: self.view)
}
}
Upvotes: 0
Views: 641
Reputation: 100541
When you want to make a dynamic View don't change frame but change constraint's constant as frame doesn't push the parent down , so make the height constraint of the view you want to animate as IBOutlet and control it's constant value
You can try
class CustomLayout: UIView {
var heightCon:NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
onLayout()
}
public func onLayout() {
print("\(frame.size.height)")
let MARGIN: CGFloat = 10
for i in 0 ..< subviews.count {
let child = subviews[i]
if i == 0 { // slide layout
child.translatesAutoresizingMaskIntoConstraints = false
child.topAnchor.constraint(equalTo: topAnchor, constant: MARGIN).isActive = true
child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 2)).isActive = true
heightCon = child.heightAnchor.constraint(equalToConstant: child.frame.size.height)
heightCon.isActive = true
}
//
public func slideAnimation() {
print("\(HEIGHT)")
isClosed = !isClosed
let ss = self.superview as! CustomLayout
ss.heightCon.constant = self.isClosed ? 0 : self.HEIGHT
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut, animations: {
ss.layoutIfNeeded()
}, completion: nil)
}
Upvotes: 1