Reputation: 331
I have created UIView
and it has one subview and it's containing shape object which are creating using UIBezierPath
, and UIView
has fixed height and width (image 1). When i click the blue color button, it should be scale down to another fixed width and height. I have applied CGAffineTransform
to subview and, i guess it is scale down properly, but since topAnchor
and leftAchor
has constant values, after scale down it is not displaying properly (image 2).I will attach my source code here and screenshots, please, analyze this and can someone suggest better approach for how to do this ? and highly appreciate ur feedback and comments.
code:
import UIKit
class ViewController: UIViewController {
var screenView: UIView!
var button: UIButton!
let widthOfScaleDownView: CGFloat = 200
let heightOfScaleDownView: CGFloat = 300
override func viewDidLoad() {
screenView = UIView(frame: .zero)
screenView.translatesAutoresizingMaskIntoConstraints = false
screenView.backgroundColor = UIColor.red
self.view.addSubview(screenView)
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: 800),
screenView.widthAnchor.constraint(equalToConstant: 600),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 100),
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 950),
button.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 350)
])
button.setTitle("click", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(scaleDownView(_:)), for: .touchUpInside)
let shapeView = UIView(frame: .zero)
shapeView.translatesAutoresizingMaskIntoConstraints = false
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let shape = UIBezierPath(roundedRect: rect, cornerRadius: 50)
let layer = CAShapeLayer()
layer.path = shape.cgPath
layer.lineWidth = 2
shapeView.layer.addSublayer(layer)
screenView.addSubview(shapeView)
NSLayoutConstraint.activate([
shapeView.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 250),
shapeView.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 150)
])
}
@IBAction func scaleDownView(_ sender: UIButton) {
let widthScale = widthOfScaleDownView/screenView.frame.width
let heightScale = heightOfScaleDownView/screenView.frame.height
screenView.subviews.forEach{ view in
view.transform = CGAffineTransform(scaleX: widthScale, y: heightScale)
}
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: heightOfScaleDownView),
screenView.widthAnchor.constraint(equalToConstant: widthOfScaleDownView),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
}
}
Upvotes: 0
Views: 1179
Reputation: 77482
Couple things...
1 - When you use CGAffineTransform
to change a view, it will automatically affect the view's subviews. So no need for a loop.
2 - Transforms are cumulative, so when "going back" to the original scale, use .identity
instead of another scale transform.
3 - You cannot set a constraint multiple times. So if you do:
screenView.heightAnchor.constraint(equalToConstant: 800)
and then you also do:
screenView.heightAnchor.constraint(equalToConstant: heightOfScaleDownView)
you will get auto-layout conflicts... the view cannot be both 800-pts tall and 300-pts tall at the same time.
Take a look at the changes I made to your code. I also added a 1-second animation to the scale transform so you can see what it's doing:
class ViewController: UIViewController {
var screenView: UIView!
var button: UIButton!
let widthOfScaleDownView: CGFloat = 200
let heightOfScaleDownView: CGFloat = 300
override func viewDidLoad() {
screenView = UIView(frame: .zero)
screenView.translatesAutoresizingMaskIntoConstraints = false
screenView.backgroundColor = UIColor.red
self.view.addSubview(screenView)
// constrain screenView
// width: 800 height: 600
// centered X and Y
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: 800),
screenView.widthAnchor.constraint(equalToConstant: 600),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 100),
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 950),
button.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 350)
])
button.setTitle("click", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(scaleDownView(_:)), for: .touchUpInside)
let shapeView = UIView(frame: .zero)
shapeView.translatesAutoresizingMaskIntoConstraints = false
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let shape = UIBezierPath(roundedRect: rect, cornerRadius: 50)
let layer = CAShapeLayer()
layer.path = shape.cgPath
layer.lineWidth = 2
shapeView.layer.addSublayer(layer)
screenView.addSubview(shapeView)
// constrain shapeView
// width: 300 height: 300
// centered X and Y in screenView
NSLayoutConstraint.activate([
shapeView.widthAnchor.constraint(equalToConstant: 300.0),
shapeView.heightAnchor.constraint(equalToConstant: 300.0),
shapeView.centerXAnchor.constraint(equalTo: screenView.centerXAnchor),
shapeView.centerYAnchor.constraint(equalTo: screenView.centerYAnchor),
])
}
@IBAction func scaleDownView(_ sender: UIButton) {
let widthScale = widthOfScaleDownView/screenView.frame.width
let heightScale = heightOfScaleDownView/screenView.frame.height
UIView.animate(withDuration: 1.0) {
// if we have already been scaled down
if widthScale == 1 {
// animate the scale transform back to original (don't add another transform)
self.screenView.transform = .identity
} else {
// animate the scale-down transform
self.screenView.transform = CGAffineTransform(scaleX: widthScale, y: heightScale)
}
}
// do NOT change screenView constraints!
}
}
Upvotes: 1