防止旋转 UIView 子视图,允许旋转同级子视图

Prevent rotating UIView subview, allow rotating sibling subviews

我有一个 UIViewController,其中包含几个 UIView 和一个 UILabel,它以全屏显示,嵌入在 UINavigationController 中。在非紧凑型高度环境(纵向)中,我希望盒子彼此堆叠,标签位于下方 - 这按预期工作。现在,当旋转到一个紧凑的高度环境(风景)时,视图当然会重新定位,所以现在盒子的宽度增加了,高度减少了。我不想要那种行为。相反,我想始终将框定位为好像 iPhone 是纵向的,但标签应该像导航栏一样旋转,以便它在当前方向上保持可读。 (这意味着在这种情况下,由于导航栏,视图的高度会受到更多限制。)

一张图片有助于更好地解释:

所以问题是,如何在 UIViewController 中仍然支持横向方向的同时防止旋转特定 UIView

我见过其他类似的问题,但他们使用过时的方法来检测特定设备方向的旋转,这与 iOS 8+ 无关。这个问题就是在考虑大小类的时候如何做到这一点。我相信解决方案将使用 viewWillTransitionToSizeanimateAlongsideTransition,但我并不完全清楚到底需要做什么。

在这种情况下我使用的是框架布局,而不是自动布局。所有视图都是以编程方式创建的。这些框实际上是 UIView 子类的子视图,该子类根据父视图的框架在框中布置和绘制内容。

UIViewController中:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let topPosition = self.topLayoutGuide.length
    boxesContainer.frame = CGRectMake(20, topPosition, self.view.frame.size.width - 40, (self.view.frame.size.height - topPosition) / 1.5)

    boxesContainer.updateLayout()
}

在盒子容器中UIView:

func updateLayout() {
    box1.frame = // something based on self.frame
    box2.frame = // ...
}

我想出了一个解决方案。本质上,我检测视图的宽度是否大于高度,如果是,则旋转包含框的视图。在第一次显示视图控制器和尺寸发生变化时执行此操作。然后进行计算以根据需要更新此视图的框架。重要的是,盒子容器视图使用其 frame.size.widthframe.size.heightmin 来放置盒子。

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    self.optimizeForSize(self.view.bounds.size)
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    //calculate frame for the box container based on current frame
    //...

    boxContainer.updateLayout()
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition({ (conext: UIViewControllerTransitionCoordinatorContext) -> Void in
        self.optimizeForSize(size)
        self.view.setNeedsLayout() //needed for iOS 8
    }, completion: nil)
}

func optimizeForSize(size: CGSize) {
    if size.width > size.height {
        self.boxContainer.transform = CGAffineTransformMakeRotation(CGFloat(-M_PI / 2.0))
    } else {
        self.colorPicker.transform = CGAffineTransformIdentity
    }
}