在iOS8中禁用单个子视图的自动旋转

7

我正在编写一个绘图应用,希望绘图视图不要旋转。与此同时,我希望其他控件可以根据设备的方向进行漂亮的旋转。在iOS 7中,我是通过以下方式解决这个问题的:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    float rotation;

    if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
        rotation = 0;
    }
    else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
        rotation = M_PI/2;
    } else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
        rotation = -M_PI/2;
    }

    self.drawingView.transform = CGAffineTransformMakeRotation(rotation);
    self.drawingView.frame = self.view.frame;
}

但是在iOS 8上,即使正确调用该功能并设置变换,它也不能防止视图旋转。

我尝试创建一个视图控制器,只是防止其视图旋转,并将其视图添加到视图层次结构中,但这时它不响应用户输入。

有什么想法吗?

谢谢!

5个回答

6

经过一番与子视图和转换协调器的斗争,我终于搞清楚了:

- (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) {
    }];
}

我所做的是,计算应用于视图的变换的逆变换,然后使用它来更改变换。此外,我将视图边界用作框架。这是因为它是全屏的。

我在iPad上保持一些相机控件的直线时遇到了类似的问题。这对我有用,稍微有所变化:我将drawingView.frame设置为一个原点为0,0,大小为传入的“size”的矩形。 - Walter
当您旋转180度时,例如从左到右横屏,整个视图将进行“桶状滚动”。这很酷。这也很令人不安。但这取决于您的选择 ;) 如果您不想要这个效果,请查看此处的答案:https://dev59.com/oIrda4cB1Zd3GeqPQ8hZ#36781371 - yuji

4

Dimitri的答案对我非常有效。以下是Swift版本的代码,以防有人需要...

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)
}

2

Swift 4 有很多更新,包括 viewWillTransition 函数。

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)
}

这是目前唯一能够完美运作的解决方案。 - Warpling

1
我已经获得了这个,检查一下: 旋转按预期工作 注意:绿色和红色视图是控制器视图的子视图。蓝色视图是红色视图的子视图。
想法 根据https://developer.apple.com/library/archive/qa/qa1890/_index.html,当视图正在过渡到新的大小时,我们需要对其应用逆旋转。 除此之外,我们还需要在旋转(横屏/竖屏)后调整约束。
实现
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

我已经尝试使用orientationDidChangeNotification进行旋转,它可以防止旋转,但无法防止调整大小和相应的错位。 - Jan
@Dimitri,没错。如果你只是应用相反的变换,它的大小就会出错。你需要自己处理大小和位置。 - InkGolem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接