xcode 子视图控制器旋转而父视图控制器不旋转
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38684986/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Child View Controller to Rotate While Parent View Controller Does Not
提问by Gizmodo
What I am Trying to Do:
我正在尝试做的事情:
Parent View that is managed by Parent View Controller SHOULD NOT ROTATE.
由父视图控制器管理的父视图不应该旋转。
Child View that is managed by Child View Controller SHOULD ROTATEto all orientations.
由子视图控制器管理的子视图应该旋转到所有方向。
What I Have Tried:
我试过的:
ParentViewController
父视图控制器
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
override func shouldAutorotate() -> Bool {
return true
}
fun addChildViewController() {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
self.childViewController = storyBoard("Child View Controller") as? ChildViewController
self .addChildViewController(self.childViewController!)
self.childViewController! .didMoveToParentViewController(self)
self.view .addSubview(self.childViewController!.view)
self.childViewController!.view.frame = CGRectMake (40, 200, 400, 250)
}
ChildViewController
子视图控制器
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .All
}
override func shouldAutorotate() -> Bool {
return true
}
Supported orientations in Xcode Deployment Info are set to all four.
Xcode 部署信息中支持的方向设置为全部四个。
What I Get:
我得到的:
None of the View's rotate. If I set the parent's rotation to all, all views rotate together. So it's all or nothing.
没有视图旋转。如果我将父级的旋转设置为全部,则所有视图将一起旋转。所以要么全有要么全无。
UPDATE
更新
When I try putting an observer for UIDeviceOrientationDidChangeNotificationand use UIDevice.currentDevice().orientation to rotate childViewusing CGAffaineTransformMakeRotate , I get desired results. HOWEVER, i.e., if I rotate to landscape, and try to pull down the notification center (or if I get a system notification), which is still in the portrait orientation (because the app is natively still left in Portrait), rotated childViewrotates back to portrait to honor the status bar/notification/notification center.
当我试图把一个观察员UIDeviceOrientationDidChangeNotification和使用UIDevice.currentDevice()方向旋转childView使用CGAffaineTransformMakeRotate,我得到想要的结果。然而,即,如果我旋转到横向,并尝试下拉通知中心(或者如果我收到系统通知),它仍然处于纵向(因为应用程序本身仍然处于纵向),旋转的childView旋转回到纵向以尊重状态栏/通知/通知中心。
Stock iOS Camera Appis the best example I can present. The main canvas does not rotate in to different orientations, yet the status bar, behind the scenes, rotates honoring device rotation. Also the subviews rotate within them selves to honor different orientations. I am trying to achieve this behavior....
Stock iOS Camera App是我能展示的最好的例子。主画布不会旋转到不同的方向,但幕后的状态栏会随着设备旋转而旋转。此外,子视图在它们内部旋转以尊重不同的方向。我正在努力实现这种行为......
采纳答案by Gizmodo
After many back and forth's with Apple them selves, and them pointing to the same link Technical Q&A QA1890, I had to do this with the flowing way:
在与 Apple 他们自己多次来回之后,他们指向相同的链接Technical Q&A QA1890,我不得不以流畅的方式做到这一点:
MotherViewController
母视图控制器
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
let deltaTransform: CGAffineTransform = coordinator.targetTransform()
let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)
var currentRotation: CGFloat = self.mainView.layer.valueForKeyPath("transform.rotation.z") as! CGFloat
// 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 += -1 * deltaAngle + 0.0001
self.mainView.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")
}, completion: {(
context: UIViewControllerTransitionCoordinatorContext) -> Void in
// Integralize the transform to undo the extra 0.0001 added to the rotation angle.
var currentTransform: CGAffineTransform = self.mainView.transform
currentTransform.a = round(currentTransform.a)
currentTransform.b = round(currentTransform.b)
currentTransform.c = round(currentTransform.c)
currentTransform.d = round(currentTransform.d)
self.mainView.transform = currentTransform
})
}
MainViewController
主视图控制器
NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name:UIDeviceOrientationDidChangeNotification, object: nil)
func orientationChanged ( notification: NSNotification){
switch UIDevice.currentDevice().orientation {
case .Portrait:
self.rotateChildViewsForOrientation(0)
case .PortraitUpsideDown:
self.rotateChildViewsForOrientation(0)
case .LandscapeLeft:
self.rotateChildViewsForOrientation(90)
case .LandscapeRight:
self.rotateChildViewsForOrientation(-90)
default:
print ("Default")
}
}
This seem to rotate the child view's while keeping the main view static. Also the app seem to know the proper orientation when notifications come in (because mother view actually rotates behind the scenes).
这似乎在保持主视图静态的同时旋转子视图。此外,当通知进来时,应用程序似乎知道正确的方向(因为母视图实际上在幕后旋转)。
Special thanks to jameskand Warif Akhand Rishifor all the help!
特别感谢jamesk和Warif Akhand Rishi的所有帮助!
回答by jamesk
The effect achieved in the Camera app is similar to using a combination of:
在相机应用程序中实现的效果类似于使用以下组合:
- the technique described in Technical Q&A QA1890for preventing a view from rotating; and
- the use of child UIStackViews to rearrange subviews on device rotation (see, e.g., the SimpleExampleViewControllerclass in the AdaptiveElementssample code shown in WWDC 2016 Session 233).
- 技术问答 QA1890 中描述的用于防止视图旋转的技术;和
- 使用子 UIStackViews 在设备旋转时重新排列子视图(例如,参见WWDC 2016 Session 233 中显示的AdaptiveElements示例代码中的SimpleExampleViewController类)。
The technical note explains that the underlying implementation of autorotation involves applying a transform directly to the application's window:
技术说明解释了自动旋转的底层实现涉及将变换直接应用于应用程序的窗口:
Autorotation is implemented by applying a rotation transform to the application's window when the system determines that the interface must rotate. The window then adjusts its bounds for the new orientation and propagates this change down through the view controller hierarchy via calls to each view controller's and presentation controller's
viewWillTransitionToSize:withTransitionCoordinator:
method.
当系统确定界面必须旋转时,通过对应用程序的窗口应用旋转变换来实现自动旋转。然后,窗口调整新方向的边界,并通过调用每个视图控制器和呈现控制器的
viewWillTransitionToSize:withTransitionCoordinator:
方法,将此更改向下传播到视图控制器层次结构。
Note that the Camera app does notopt out of autorotation: when using the Camera app, the orientations of Notification Center and Control Center reflect the device orientation. It is only particular views within the Camera app that appear to opt out of autorotation. In fact, this answersuggests that such apps typically utilise the videoGravity
and videoOrientation
properties provided by AVFoundation classes such as AVCaptureVideoPreviewLayerand AVCaptureConnection.
请注意,相机应用程序并没有选择自动旋转出:使用相机应用程序时,通知中心和控制中心的方向反映设备的方向。只有相机应用程序中的特定视图似乎选择退出自动旋转。事实上,这个答案表明此类应用程序通常利用AVFoundation 类提供的videoGravity
和videoOrientation
属性,例如AVCaptureVideoPreviewLayer和AVCaptureConnection。
The approach you should take depends on whether you only want to stop a parent view from rotating or whether you want to stop a container view controller such as UINavigationController from rotating (i.e. lock device orientation).
您应该采用的方法取决于您是只想阻止父视图旋转,还是要阻止容器视图控制器(例如 UINavigationController)旋转(即锁定设备方向)。
If the former, use the technique described in the technical note to fix the orientation of the parent view, and (for iOS 9) wrap rotating subviews in a UIStackView and use the axis
property to rearrange the subviews on device rotation, or (for iOS 8) manually rotate the subviews on device rotation (as to which see this answerand this sample code).
如果是前者,请使用技术说明中描述的技术来固定父视图的方向,并且(对于 iOS 9)将旋转子视图包装在 UIStackView 中并使用该axis
属性在设备旋转时重新排列子视图,或者(对于 iOS 8 ) 在设备旋转时手动旋转子视图(参见此答案和此示例代码)。
If the latter, the approach you're taking is on the right track. For sample code, see this answer. You'll need to share your code and more information about your view controller hierarchy for us to diagnose quirks involving Notification Center.
如果是后者,则您采用的方法是正确的。有关示例代码,请参阅此答案。你需要分享你的代码和更多关于你的视图控制器层次结构的信息,以便我们诊断涉及通知中心的怪癖。
Addendum: The reason why calls to shouldAutorotate
and supportedInterfaceOrientations
were not being propagated to child view controllers was explained in Technical Q&A QA1688:
附录:技术问答 QA1688 中解释了调用shouldAutorotate
和supportedInterfaceOrientations
未传播到子视图控制器的原因:
Beginning with iOS 6, only the topmost view controller (alongside the
UIApplication
object) participates in deciding whether to rotate in response to a change of the device's orientation. More often than not, the view controller displaying your content is a child ofUINavigationController
,UITabBarController
, or a custom container view controller. You may find yourself needing to subclass a container view controller in order to modify the values returned by itssupportedInterfaceOrientations
andshouldAutorotate
methods.
从 iOS 6 开始,只有最顶层的视图控制器(与
UIApplication
对象一起)参与决定是否旋转以响应设备方向的变化。更多的,往往不是你的显示内容视图控制器是一个孩子UINavigationController
,UITabBarController
或自定义容器视图控制器。您可能会发现自己需要对容器视图控制器进行子类化,以修改其supportedInterfaceOrientations
和shouldAutorotate
方法返回的值。
In Swift, you can override those methods by using extensions.
在 Swift 中,您可以使用 extensions来覆盖这些方法。
回答by Warif Akhand Rishi
From iOS developer library Technical Q&A
来自 iOS 开发者库技术问答
Autorotation is implemented by applying a rotation transform to the application's window when the system determines that the interface must rotate. The window then adjusts its bounds for the new orientation and propagates this change down through the view controller hierarchy via calls to each view controller's and presentation controller's -viewWillTransitionToSize:withTransitionCoordinator: method. Invocations of this method are provided a transition coordinator object containing the delta of the window's current transform and new transform, which can be retrieved by sending a -targetTransform message to the transition coordinator. Your view controller or presentation controller can derive the appropriate inverse transform and apply it to the target view. This nullifies the effect of the window transform on that particular view and gives the appearance that the view has remained fixed in its current orientation as the rest of the interface rotates normally.
当系统确定界面必须旋转时,通过对应用程序的窗口应用旋转变换来实现自动旋转。然后窗口调整其新方向的边界,并通过调用每个视图控制器和演示控制器的 -viewWillTransitionToSize:withTransitionCoordinator: 方法,将此更改向下传播到视图控制器层次结构。此方法的调用提供了一个转换协调器对象,该对象包含窗口的当前变换和新变换的增量,可以通过向过渡协调器发送 -targetTransform 消息来检索该增量。您的视图控制器或表示控制器可以派生适当的逆变换并将其应用于目标视图。
i.e. if you have a view named noRotatingView
即,如果您有一个名为的视图 noRotatingView
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
noRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
let deltaTransform = coordinator.targetTransform()
let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))
var currentRotation = self.noRotatingView.layer.valueForKeyPath("transform.rotation.z")?.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
self.noRotatingView.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")
}) { (UIViewControllerTransitionCoordinatorContext) -> Void in
var currentTransform = self.noRotatingView.transform;
currentTransform.a = round(currentTransform.a);
currentTransform.b = round(currentTransform.b);
currentTransform.c = round(currentTransform.c);
currentTransform.d = round(currentTransform.d);
self.noRotatingView.transform = currentTransform;
}
}
I've tried to make a demo project at https://github.com/rishi420/TestCameraRotation
我试图在https://github.com/rishi420/TestCameraRotation制作一个演示项目