J'ai un controller de vue avec un controller de vue enfant.
tab bar controller | | nav controller | | UIPageViewController (should rotate) | | A (Video Player) (shouldn't rotate) | | B (Controls overlay) (should rotate)
A devrait être obligé de restr portrait à tout moment, mais B devrait pouvoir tourner librement.
Je sais que shouldAutorotate
s'applique à tous les controllers de vue et à ses enfants, mais y a-t-il un moyen de contourner ce shouldAutorotate
? Il semble que je pourrais utiliser shouldAutorotateToInterfaceOrientation
, mais cela est bloqué dans iOS 8.
Je souhaite que le lecteur video rest statique (les videos horizontales sont donc toujours horizontales, quelle que soit l'orientation de l'appareil), tandis que la superposition de la sous-vue des commands est autorisée à tourner librement.
J'utilise Swift.
J'ai eu ce problème exact, et j'ai découvert rapidement qu'il y a beaucoup de mauvais conseils qui circulent autour de l'autorotation, en particulier parce que iOS 8 le gère différemment des versions précédentes.
Tout d'abord, vous ne voulez pas appliquer une contre- UIDevice
manuellement ou vous abonner à des UIDevice
orientation UIDevice
. Faire une contre- rotation entraînera toujours une animation disgracieuse, et l'orientation du périphérique n'est pas toujours la même que celle de l' interface . Idéalement, vous souhaitez que l'aperçu de la camera rest réellement figé et que l'interface user de votre application corresponde à l'orientation et à la taille de la barre d'état au fur et à mesure qu'elles changent, exactement comme l'application Camera native.
Lors d'un changement d'orientation dans iOS 8, la window elle-même pivote plutôt que la ou les vues qu'elle contient. Vous pouvez append les vues de plusieurs controllers de vue à une seule UIWindow
, mais seul rootViewController
aura l'opportunité de répondre via shouldAutorotate()
. Même si vous faites la décision de rotation au niveau du controller de vue, c'est la window parente qui tourne réellement, faisant ainsi pivoter toutes ses sous-vues (y compris celles des autres controllers de vue).
La solution est deux UIWindow
empilés les uns sur les autres, chacun tournant (ou pas) avec son propre controller de vue racine. La plupart des applications n'en ont qu'une, mais il n'y a aucune raison de ne pas en avoir deux et de les superposer comme n'importe quelle autre sous-class UIView
.
Voici une preuve de concept, que j'ai aussi mis sur GitHub ici . Votre cas particulier est un peu plus compliqué parce que vous avez une stack de controllers de vue contenant, mais l'idée de base est la même. Je vais aborder quelques points spécifiques ci-dessous.
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var cameraWindow: UIWindow! var interfaceWindow: UIWindow! func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { let screenBounds = UIScreen.mainScreen().bounds let inset: CGFloat = fabs(screenBounds.width - screenBounds.height) cameraWindow = UIWindow(frame: screenBounds) cameraWindow.rootViewController = CameraViewController() cameraWindow.backgroundColor = UIColor.blackColor() cameraWindow.hidden = false interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset)) interfaceWindow.rootViewController = InterfaceViewController() interfaceWindow.backgroundColor = UIColor.clearColor() interfaceWindow.opaque = false interfaceWindow.makeKeyAndVisible() return true } }
Définir un encart négatif sur interfaceWindow
rend légèrement plus grand que les limites de l'écran, masquant efficacement le masque rectangular noir que vous verriez autrement. Normalement, vous ne le remarquerez pas parce que le masque tourne avec la window, mais puisque la window de la camera est fixe, le masque devient visible dans les coins pendant la rotation.
class CameraViewController: UIViewController { override func shouldAutorotate() -> Bool { return false } }
Exactement ce que vous attendez ici, il suffit d'append votre propre configuration pour AVCapturePreviewLayer
.
class InterfaceViewController: UIViewController { var contentView: UIView! override func viewDidLoad() { super.viewDidLoad() contentView = UIView(frame: CGRectZero) contentView.backgroundColor = UIColor.clearColor() contentView.opaque = false view.backgroundColor = UIColor.clearColor() view.opaque = false view.addSubview(contentView) } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() let screenBounds = UIScreen.mainScreen().bounds let offset: CGFloat = fabs(screenBounds.width - screenBounds.height) view.frame = CGRectOffset(view.bounds, offset, offset) contentView.frame = view.bounds } override func supportedInterfaceOrientations() -> Int { return Int(UIInterfaceOrientationMask.All.rawValue) } override func shouldAutorotate() -> Bool { return true } }
La dernière astuce consiste à annuler l'incrustation négative que nous avons appliquée à la window, ce que nous obtenons en décalant la view
la même quantité et en traitant contentView
comme la vue principale.
interfaceWindow.rootViewController
est votre controller de barre d'tabs, qui contient à son tour un controller de navigation, etc. Toutes ces vues doivent être transparentes lorsque le controller de votre camera apparaît pour que la window de la camera puisse apparaître au-dessous. Pour des raisons de performances, vous pouvez envisager de les laisser opaques et de ne les rendre transparentes que lorsque la camera est en cours d'utilisation, et régler la window de la camera sur hidden
quand elle ne l'est pas (tout en fermant la session de capture).
Désolé de postr un roman; Je n'ai pas vu cela ailleurs et il m'a fallu du time pour le comprendre, j'espère que cela vous aidera, vous et tous ceux qui essaient d'avoir le même comportement. Même l'exemple d'application AVCam
d'Apple ne le gère pas correctement.
L'exemple de rapport que j'ai posté inclut également une version avec la camera déjà installée. Bonne chance!
Vous pouvez essayer ceci –
Objective -C code si vous avez son alternative dans swift:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { if ()//Place your condition here like if A is visible { return UIInterfaceOrientationMaskPortrait; } return UIInterfaceOrientationMaskAll; }
Vous pouvez vous abonner aux notifications de changement de rotation et définir manuellement la masortingce de transformation de rotation sur la sous-vue que vous voulez faire pivoter.
Je ne suis pas sûr, mais je pense que vous pourriez créer une propre class pour votre sous-vue et replace la méthode shouldAutorotate
etc. De cette façon, il devrait replace le shouldAutorotate
du parent-viewcontroller.
Réponse courte: Non, tous les controllers et vues visibles tournent (ou ne pivotent pas) set.
Longue réponse:
D'abord, vous devez implémenter des fonctions de décision autorotate dans le controller racine; cela peut signifier faire une sous-class de controller de nav.
Vous pouvez pirater le comportement souhaité en faisant en sorte que le parent regarde en autorotation, mais qu'il se fasse manuellement pivoter pour qu'il apparaisse non pivoté.
Ou, vous ne pouvez pas autorotate, mais écoutez les notifications que le périphérique physique a fait pivoter, et tournez manuellement toutes les vues que vous voulez, par exemple: Répliquer la rotation de l'application de la camera à l'iPhone IOS 6 paysage
Voir aussi, fyi:
Comment forcer un UIViewController à l'orientation Portrait dans iOS 6
Méthode shouldAutoRotate non appelée dans iOS6
iOS6: supportedInterfaceOrientations ne fonctionne pas (est invoqué mais l'interface tourne toujours)
Comment implémenter la rotation de UIViewController en réponse aux changements d'orientation?
La réponse la plus simple et la plus directe à cette question est de regarder l'exemple de code AVCam d'Apple. Les parties keys pour moi étaient que:
layerClass
est AVCaptureVideoPreviewLayer
. videoOrientation
de la connection de AVCaptureVideoPreviewLayer pour correspondre à statusBarOrientation de l'application lorsque la vue est présentée, essentiellement viewWillAppear(_:)
. videoOrientation
pour correspondre à UIDevice.currentDevice().orientation
dans viewWillTransitionToSize(_:withTransitionCoordinator:)
. J'ai implémenté l'approche de window d'arrière-plan décrite par jstn et cela a fonctionné assez bien, mais la réalité est que c'est beaucoup plus compliqué que nécessaire. AVCam fonctionne très bien et a une approche relativement simple.