swiftpositioncgpointanchorpoint

create center anchor point of object


My swift code below is trying to rotate a imageview around another imageview. I want the rotation to work just like a game spinner. A link to something close to what I am looking for is https://www.orientaltrading.com/large-spinner-wheel-stand-up-a2-13846120.fltr. Right now my code is not doing that you can see in the gif below what the code is currently doing. The gif is not doing what I want it to. I want the bottom anchor of the pink box to be its center bottom anchor which is attached to the center of the uiviewcontroller. That rotates 180 degrees in one way. Just like a game spinner in the link above.

enter image description here

import UIKit

class ViewController: UIViewController {
    var box = UIImageView()
    var box1 = UIImageView()
    override func viewDidLoad() {
        super.viewDidLoad()

        box.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(box)
        box.backgroundColor = .systemPink
        box1.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(box1)
        box1.backgroundColor = .purple

        self.box.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0.5))

        Rotate()

        NSLayoutConstraint.activate([
            box.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25),
            box.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.25),
            box.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            box.bottomAnchor.constraint(equalTo: view.centerYAnchor),

            box1.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.05),
            box1.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.05),
            box1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            box1.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
        // Do any additional setup after loading the view.
    }


    func Rotate(){

        let   rotation = UIViewPropertyAnimator(duration: 25, curve: .linear, animations: {

            self.box.transform = self.box.transform.rotated(by: CGFloat.pi/0.94)

        })
        rotation.startAnimation()

    }





}

extension UIView{
    func setAnchorPoint(anchorPoint: CGPoint) {

        var newPoint = CGPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
        var oldPoint = CGPoint(x: self.bounds.size.width * self.layer.anchorPoint.x, y: self.bounds.size.height * self.layer.anchorPoint.y)

        newPoint = newPoint.applying(self.transform)
        oldPoint = oldPoint.applying(self.transform)

        var position : CGPoint = self.layer.position

        position.x -= oldPoint.x
        position.x += newPoint.x;

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

        self.layer.position = position;
        self.layer.anchorPoint = anchorPoint;
    }
}

Solution

  • There are two problems with your code:

    1. Anchor Point should be equal (0.5, 1). (Center of bottom).
    2. You are settings anchor point in inappropriate time. Your code for transforming position is correct, but bounds are (0, 0, 0, 0).

    Delete setAnchorPoint and Rotate calls from view did load and add method like this:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        view.setNeedsLayout()
        view.layoutIfNeeded()
    
        self.box.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 1))
        Rotate()
    }
    

    In viewWillAppear size of controllers view has been already set. And calling view.setNeedsLayout() and view.layoutIfNeeded() layout your boxes. You can check box frame before and after these calls.