読者です 読者をやめる 読者になる 読者になる

SwiftでAuto Layoutした時の座標の取り方

SwiftでAuto Layoutを設定した場合、その直後ではframeやboundsプロパティから正しい座標が取得できません。

例えば、ViewControllerにこんな感じでUIViewを作ると、4辺が全て0のRectが返されます。

override func viewDidLoad() {
    super.viewDidLoad()
    
    let viewA = UIView()
    viewA.setTranslatesAutoresizingMaskIntoConstraints(false)
    self.view.addSubview(viewA)
    let constraints = [
        NSLayoutConstraint(item: viewA, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 10), //x=10
        NSLayoutConstraint(item: viewA, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1.0, constant: 10), //y=10
        NSLayoutConstraint(item: viewA, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100), //幅=10
        NSLayoutConstraint(item: viewA, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50), //高さ=10
    ]
    self.view.addConstraints(constraints)
    
    println(viewA.frame) <<(0.0,0.0,0.0,0.0)
}


そんな時は、layoutIfNeeded()メソッドを呼ぶと正しい座標が呼ばれるようになります。

    viewA.layoutIfNeeded()
    println(viewA.frame) <<(10.0,10.0,100.0,50.0)

ただ、UIViewに対して直接layoutIfNeeded()を呼ぶと、Viewが入れ子になっていた場合には直近のsuperviewからの座標しか再計算されません。
viewAのsubviewの画面上の座標を計算しようとしても正しく計算されません。

    subView.layoutIfNeeded()
    let displayRect = subView.convertRect(subView.bounds, toView: self.view) //self.viewのselfはViewController
    println(displayRect) <<直接の親Viewからの相対位置が返される


これを解決するには、ViewControllerのviewプロパティのlayoutIfNeeded()メソッドを呼ぶことです。

    self.view.layoutIfNeeded()
    let displayRect = subView.convertRect(subView.bounds, toView: self.view) //self.viewのselfはViewController
    println(displayRect)  <<画面上の絶対座標が取れるようになる

swift-layout

ちなみに、先々週から作っているswift-layoutでは、displayRect()で画面上の絶対座標を取得できます。

    let viewA = Layout.registUIView(superview: self.view)
        .left(10).fromSuperviewLeft()
        .top(10).fromSuperviewTop()
        .width(100)
        .height(50)
        .backgroundColor(UIColor.redColor())

    let subView = Layout.registUIView(superview: viewA.target)
        .left(30).fromSuperviewLeft()
        .top(15).fromSuperviewTop()
        .width(20)
        .height(20)
        .backgroundColor(UIColor.blueColor())

    println(subView.displayRect()) //(40.0,25.0,20.0,20.0)