Disable autorotate on a single subview in iOS8

后端 未结 5 935
遇见更好的自我
遇见更好的自我 2020-12-10 07:33

I\'m writing a drawing app and I don\'t want the drawing view to rotate. At the same time, I want other controls to rotate nicely depending on the orientation of the device.

相关标签:
5条回答
  • Swift 4 has a lot of updates, including the viewWillTransition function.

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
    
        let targetRotation = coordinator.targetTransform
        let inverseRotation = targetRotation.inverted()
    
        coordinator.animate(alongsideTransition: { context in
            self.drawingView.transform = self.drawingView.transform.concatenating(inverseRotation)
            self.drawingView.frame = self.view.bounds
            context.viewController(forKey: UITransitionContextViewControllerKey.from)
        }, completion: nil)
    }
    
    0 讨论(0)
  • 2020-12-10 08:11

    Dimitri's answer worked perfectly for me. This is the swift version of the code in case someone needs it...

    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
      let targetRotation = coordinator.targetTransform()
      let inverseRotation = CGAffineTransformInvert(targetRotation)
    
      coordinator.animateAlongsideTransition({ context in
        self.drawingView.transform = CGAffineTransformConcat(self.drawingView.transform, inverseRotation)
        self.drawingView.frame = self.view.bounds
        context.viewControllerForKey(UITransitionContextFromViewControllerKey)
      }, completion: nil)
    }
    
    0 讨论(0)
  • 2020-12-10 08:18

    In iOS 8 transforms aren't applied to individual views owned by view controllers. Instead the rotation transforms are applied to the UIWindow. The result is that developers never see a rotation being applied, but rather a resize.

    In iOS 8 you can either override the callbacks for size class changes and perform your own transform there, or you can get the orientation events as described here: https://developer.apple.com/library/prerelease/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/motion_event_basics/motion_event_basics.html.

    0 讨论(0)
  • 2020-12-10 08:25

    Okay after some fighting with subviews and transitionCoordinators I've finally figured it out:

    - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
        CGAffineTransform targetRotation = [coordinator targetTransform];
        CGAffineTransform inverseRotation = CGAffineTransformInvert(targetRotation);
    
        [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    
            self.drawingView.transform = CGAffineTransformConcat(self.drawingView.transform, inverseRotation);
    
            self.drawingView.frame = self.view.bounds;
        } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        }];
    }
    

    What I do is, I calculate the inverse of the transform applied to the view and then use it to change the transform. furthermore I change the frame with the view bounds. This is due to it being full screen.

    0 讨论(0)
  • 2020-12-10 08:31

    I managed to obtain this, check:

    rotation working as desired

    Note: The green and the red views are subviews of the controller's view. The blue view is subview of the red view.

    Idea According to https://developer.apple.com/library/archive/qa/qa1890/_index.html, we need to apply an inverse rotation to the view when it is transitioning to a new size. In addition to that, we need to adjust the constraints after rotation (landscape/portrait).

    Implementation

    class MyViewController: UIViewController {
    
        var viewThatShouldNotRotate = UIView()
    
        var view2 = UIView()
    
        var insiderView = UIView()
    
        var portraitConstraints: [NSLayoutConstraint]!
    
        var landscapeConstraints: [NSLayoutConstraint]!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            viewThatShouldNotRotate.backgroundColor = .red
    
            view2.backgroundColor = .green
    
            insiderView.backgroundColor = .blue
    
            view.addSubview(viewThatShouldNotRotate)
            view.addSubview(view2)
    
    
            viewThatShouldNotRotate.addSubview(insiderView)
            portraitConstraints = createConstraintsForPortrait()
            landscapeConstraints = createConstraintsForLandscape()
        }
    
        func createConstraintsForLandscape() -> [NSLayoutConstraint] {
            return NSLayoutConstraint.autoCreateConstraintsWithoutInstalling {
                viewThatShouldNotRotate.autoMatch(.height, to: .width, of: view)
                viewThatShouldNotRotate.autoMatch(.width, to: .height, of: view)
                viewThatShouldNotRotate.autoCenterInSuperview()
    
                view2.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(), excludingEdge: .top)
                view2.autoSetDimension(.height, toSize: 100)
    
                insiderView.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom)
                insiderView.autoSetDimension(.height, toSize: 100)
            }
        }
    
        func createConstraintsForPortrait() -> [NSLayoutConstraint] {
            return NSLayoutConstraint.autoCreateConstraintsWithoutInstalling {
                viewThatShouldNotRotate.autoMatch(.height, to: .height, of: view)
                viewThatShouldNotRotate.autoMatch(.width, to: .width, of: view)
                viewThatShouldNotRotate.autoCenterInSuperview()
                view2.autoPinEdges(toSuperviewMarginsExcludingEdge: .top)
                view2.autoSetDimension(.height, toSize: 100)
                insiderView.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom)
                insiderView.autoSetDimension(.height, toSize: 100)
            }
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            if view.bounds.size.width > view.bounds.size.height {
                // landscape
                portraitConstraints.forEach {$0.autoRemove()}
                landscapeConstraints.forEach { $0.autoInstall() }
            } else {
                landscapeConstraints.forEach {$0.autoRemove()}
                portraitConstraints.forEach { $0.autoInstall() }
            }
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
    
            let viewToRotate: UIView = viewThatShouldNotRotate
    
            coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
                let deltaTransform = coordinator.targetTransform
                let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))
    
                var currentRotation = (viewToRotate.layer.value(forKeyPath: "transform.rotation.z") as! NSNumber).floatValue
    
                // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
                currentRotation = currentRotation + (-1 * Float(deltaAngle)) + 0.0001
                viewToRotate.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")
    
            }) { (UIViewControllerTransitionCoordinatorContext) -> Void in
    
                var currentTransform = viewToRotate.transform;
                currentTransform.a = round(currentTransform.a);
                currentTransform.b = round(currentTransform.b);
                currentTransform.c = round(currentTransform.c);
                currentTransform.d = round(currentTransform.d);
                viewToRotate.transform = currentTransform;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题