LoginSignup
27
24

More than 5 years have passed since last update.

CALayer の anchorPoint を変更しつつ表示位置は維持する

Last updated at Posted at 2016-07-12

CALayer の anchorPoint を変更しつつ表示位置は維持する

わかりにくい表題になってしまっていますが、CALayer の基準点である anchorPoint をずらすと表示位置もずれてしまうのをなんとかするための方法です。回転軸をずらしたいけど表示位置は維持したい、とかそういった場面に向いています。anchorPoint あるあるなのでわかる方にはわかるかと。

Stackoverflow 先生で以下の素晴らしいコードを見つけました。

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}

このように使えます。

setAnchorPoint(CGPointMake(1.0, 1.0), forView: aView)

aView の anchorPoint を (1.0, 1.0) に移動させますが、例えば回転アニメーションを適用すると表示位置はそのままで(※)右下を中心に動くようになります。求めていたのはコレでした。

※実際には anchorPoint の移動に合わせて相応の量を position で移動させて相殺しています。

追記:extension にした

CALayer+AnchorPoint
extension CALayer {
    func setAnchorPoint(newAnchorPoint: CGPoint, forView view: UIView) {
        var newPoint = CGPointMake(self.bounds.size.width * newAnchorPoint.x, self.bounds.size.height * newAnchorPoint.y)
        var oldPoint = CGPointMake(self.bounds.size.width * self.anchorPoint.x, self.bounds.size.height * self.anchorPoint.y)

        newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
        oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

        var position = self.position
        position.x -= oldPoint.x
        position.x += newPoint.x

        position.y -= oldPoint.y
        position.y += newPoint.y

        position.x = newPoint.x
        position.y = newPoint.y

        self.anchorPoint = newAnchorPoint
        self.position = position
    }
}

追記:position がリセットされる謎現象

とあるビューのサブビューにこのアンカーポイントずらし技を適用したまでは良かったのですが、同一階層の別ビューで何か Auto Layout が実行されるとこっちのレイヤーの position がなぜかリセットされるという、説明するのも一苦労な謎現象に悩まされました。

結果的にはコードを以下のように改変してから、『とあるビュー』で layoutSubviews() をオーバーライド、そこで『サブビュー』に対してアンカーポイントを再設定する、という荒技でなんとか解決しました。

CALayer+AnchorPoint_2
extension CALayer {
    func setAnchorPoint(newAnchorPoint: CGPoint, forView view: UIView) {
        var newPoint = CGPointMake(self.bounds.size.width * newAnchorPoint.x, self.bounds.size.height * newAnchorPoint.y)

        newPoint = CGPointApplyAffineTransform(newPoint, view.transform)

        var position = self.position

        position.x = newPoint.x
        position.y = newPoint.y

        self.anchorPoint = newAnchorPoint
        self.position = position
    }
}
override func layoutSubviews() {
    super.layoutSubviews()

    // ここでアンカーポイントを再設定しないとたまに position がずれてしまう
    subView.layer.setAnchorPoint(anchorPoint, forView: subView)
}
// レイアウトが変わりそうな場面でこれを実行
layoutIfNeeded()
27
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
24