イベントの複数のUI要素の複数の制約を更新するソリューションを見つけようとしています。非アクティブ化、変更、および制約の再アクティブ化の例をいくつか見てきましたが、この方法は、私が作業している24のアンカーに対しては実用的ではないようです。
私の一連の変更の1つ:
ticketContainer.translatesAutoresizingMaskIntoConstraints = false
ticketContainer.topAnchor.constraintEqualToAnchor(self.topAnchor).active = true
ticketContainer.leftAnchor.constraintEqualToAnchor(self.rightAnchor, constant: 20).active = true
ticketContainer.widthAnchor.constraintEqualToConstant(200.0).active = true
ticketContainer.leftAnchor.constraintEqualToAnchor(self.leftAnchor, constant: 20).active = true
ticketContainer.widthAnchor.constraintEqualToConstant(100.0).active = true
プロパティへのレイアウトアンカーを使用して作成した関連する制約を保存し、定数を変更しようとしましたか?例えば。
var ticketTop : NSLayoutConstraint?
func setup() {
ticketTop = ticketContainer.topAnchor.constraintEqualToAnchor(self.topAnchor, constant:100)
ticketTop.active = true
}
func update() {
ticketTop?.constant = 25
}
拡張機能の作成の好みに応じて、プロパティを使用せずに、より簡潔な使用法を支援するためにNSLayoutAnchor
およびUIView
に拡張メソッドを作成する、よりエレガントなアプローチを以下に示します。
まず、次のようにNSLayoutAnchor
に拡張機能を記述します。
extension NSLayoutAnchor {
func constraintEqualToAnchor(anchor: NSLayoutAnchor!, constant:CGFloat, identifier:String) -> NSLayoutConstraint! {
let constraint = self.constraintEqualToAnchor(anchor, constant:constant)
constraint.identifier = identifier
return constraint
}
}
この拡張により、アンカーから作成するのと同じメソッド呼び出しで制約に識別子を設定できます。 Appleドキュメンテーションは、XAxisアンカー(左、右、先頭など)がYAxisアンカー(上部、下部など)で制約を作成できないことを意味しますが、これが実際に真であることに気づかないでください。そのタイプのコンパイラチェックが必要な場合は、NSLayoutXAxisAnchor
、NSLayoutYAxisAnchor
、およびNSLayoutDimension
(for幅と高さの制約)は、同じ軸のアンカータイプの要件を強制します。
次に、UIView
に拡張機能を記述して、識別子による制約を取得します。
extension UIView {
func constraint(withIdentifier:String) -> NSLayoutConstraint? {
return self.constraints.filter{ $0.identifier == withIdentifier }.first
}
}
これらの拡張機能を配置すると、コードは次のようになります。
func setup() {
ticketContainer.topAnchor.constraintEqualToAnchor(anchor: self.topAnchor, constant:100, identifier:"ticketTop").active = true
}
func update() {
self.constraint(withIdentifier:"ticketTop")?.constant = 25
}
識別子にマジックストリング名の代わりに定数または列挙を使用すると、上記よりも改善されることに注意してください。
ビューの制約を反復処理し、一致するアイテムとアンカーを確認できます。ディメンションの制約でない限り、制約はビューのスーパービューに適用されることに注意してください。ビューの1つのアンカーに関するすべての制約を見つけることができるヘルパーコードをいくつか作成しました。
import UIKit
class ViewController: UIViewController {
let label = UILabel()
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Constraint Finder"
label.translatesAutoresizingMaskIntoConstraints = false
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addSubview(imageView)
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
label.widthAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true
imageView.topAnchor.constraint(equalTo: label.bottomAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 60).isActive = true
imageView.widthAnchor.constraint(equalTo: label.widthAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
print("Label's top achor constraints: \(label.constraints(on: label.topAnchor))")
print("Label's width achor constraints: \(label.constraints(on: label.widthAnchor))")
print("ImageView's width achor constraints: \(imageView.constraints(on: imageView.widthAnchor))")
}
}
public extension UIView {
public func constraints(on anchor: NSLayoutYAxisAnchor) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return superview.constraints.filtered(view: self, anchor: anchor)
}
public func constraints(on anchor: NSLayoutXAxisAnchor) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return superview.constraints.filtered(view: self, anchor: anchor)
}
public func constraints(on anchor: NSLayoutDimension) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return constraints.filtered(view: self, anchor: anchor) + superview.constraints.filtered(view: self, anchor: anchor)
}
}
extension NSLayoutConstraint {
func matches(view: UIView, anchor: NSLayoutYAxisAnchor) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
func matches(view: UIView, anchor: NSLayoutXAxisAnchor) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
func matches(view: UIView, anchor: NSLayoutDimension) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
}
extension Array where Element == NSLayoutConstraint {
func filtered(view: UIView, anchor: NSLayoutYAxisAnchor) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
func filtered(view: UIView, anchor: NSLayoutXAxisAnchor) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
func filtered(view: UIView, anchor: NSLayoutDimension) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
}
extension UIView {
func add(view: UIView, left: CGFloat, right: CGFloat, top: CGFloat, bottom: CGFloat) {
view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(view)
view.leftAnchor.constraint(equalTo: self.leftAnchor, constant: left).isActive = true
view.rightAnchor.constraint(equalTo: self.rightAnchor, constant: right).isActive = true
view.topAnchor.constraint(equalTo: self.topAnchor, constant: top).isActive = true
view.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: bottom).isActive = true
}
}
headerView.add(view: headerLabel, left: 20, right: 0, top: 0, bottom: 0)