UIView autorotation issue

2019-08-25 12:12发布

问题:

I am getting issues in autorotation of a UIView which is nothing but a red line of width 4 and height half of superview's height in landscape mode, and in portrait mode the height is 4 points and width is half of superview width. You can see the issue in the gif. As I rotate from landscape to portrait, the effect of autorotation is not smooth and with distortions.

Here is the code. What am I doing wrong?

private var myView:UIView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    myView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width/2, height: 4))
    myView.backgroundColor = UIColor.red
    view.addSubview(myView)

    myView.center = CGPoint(x: view.bounds.width/2, y: view.bounds.height/2)


}

private func layoutMyView() {

    let orientation = UIApplication.shared.statusBarOrientation

    if orientation == .landscapeLeft || orientation == .landscapeRight {
        myView.frame = CGRect(x: 0, y: 0, width: view.bounds.width/2, height: 4)
    } else {
        myView.frame = CGRect(x: 0, y: 0, width: 4, height: view.bounds.height/2)
    }

    myView.center = CGPoint(x: view.bounds.width/2, y: view.bounds.height/2)
}


override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

    let orientation = UIApplication.shared.statusBarOrientation

    NSLog("View will transition to \(size), \(orientation.rawValue)")

    if size.width < size.height {
        NSLog("Portrait mode")
    } else {
        NSLog("Landscape mode")
    }

    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { [unowned self] (_) in

        let orient = UIApplication.shared.statusBarOrientation

         self.layoutMyView()

        }, completion: { [unowned self] (UIViewControllerTransitionCoordinatorContext) -> Void in

            let orient = UIApplication.shared.statusBarOrientation
            self.layoutMyView()
            NSLog("rotation completed to \(orient.rawValue), \(self.myView.frame)")
    })
}

回答1:

There are a few potential options:

  1. In animate(alongsideTransition:completion:), do a keyframe animation to set mid animation point so you don’t end up with that curious boxy feel mid animation. E.g. this shrink it down to a 4x4 box

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
    
        let midpointOrigin = CGPoint(x: (view.bounds.midX + view.bounds.midY) / 2 - 2,
                             y: (view.bounds.midX + view.bounds.midY) / 2 - 2)
    
        coordinator.animate(alongsideTransition: { _ in
            UIView.animateKeyframes(withDuration: coordinator.transitionDuration, delay: 0, animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    self.myView.frame = CGRect(origin: midpointOrigin, size: CGSize(width: 4, height: 4))
                }
    
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    self.layoutMyView()
                }
            })
        })
    }
    

    There are many possible variations on this theme. This still animates the view, but gives it a more “deliberate” sort of vibe and doesn’t lose the “line” feel of it.

  2. Instead of animating the frame, you could animate the transform of this view, possibly rotating it in the opposite direction that the view is rotating.

  3. You could run a CADisplayLink while transition is in progress, and then update the frame to whatever you want as the animation progresses.