it-swarm.com.ru

Есть ли документированный способ установки ориентации iPhone?

У меня есть приложение, в котором я хотел бы поддерживать поворот устройства в определенных видах, но другие не имеют особого смысла в режиме «Пейзаж», поэтому, меняя представления, я хотел бы установить поворот в портретный режим.

В UIDevice есть недокументированный установщик свойств, который делает свое дело, но, очевидно, генерирует предупреждение компилятора и может исчезнуть с будущей версией SDK.

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

Есть ли документированные способы форсировать ориентацию?

Обновление: Я подумал, что приведу пример, так как я не ищу shouldAutorotateToInterfaceOrientation, поскольку я это уже реализовал.

Я хочу, чтобы мое приложение поддерживало альбомную ориентацию и портретную ориентацию в представлении 1, но только вертикальную ориентацию в представлении 2. Я уже реализовал mustAutorotateToInterfaceOrientation для всех представлений, но если пользователь находится в горизонтальном режиме в представлении 1, а затем переключается на представление 2, я хочу принудительно вызвать телефон, чтобы повернуть обратно в портрет.

94
Dave Verwer

Это больше не проблема на более позднем iPhone 3.1.2 SDK. Теперь кажется, что он выполняет заданную ориентацию представления, возвращаемого обратно в стек. Это, вероятно, означает, что вам нужно будет обнаружить более старые версии iPhone OS и применять setOrientation только тогда, когда это происходит до последней версии.

Неясно, поймет ли статический анализ Apple, что вы работаете над старыми ограничениями SDK. Apple лично сказал мне убрать вызов метода в моем следующем обновлении, так что я пока не уверен, пройдет ли процесс одобрения для старых устройств.

6
John K

Это долго после факта, но на всякий случай приходит кто-то, кто не использует контроллер навигации и/или не хочет использовать недокументированные методы:

UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];

Достаточно представить и отклонить контроллер вида Vanilla.

Очевидно, вам все равно нужно будет подтвердить или отклонить ориентацию в переопределении shouldAutorotateToInterfaceOrientation. Но это приведет к тому, что система должна снова вызывать Autorotate ....

101
Josh

Из того, что я могу сказать, метод setOrientation: не работает (или, возможно, больше не работает). Вот что я делаю, чтобы сделать это:

во-первых, поместите это определение вверху вашего файла, прямо под #imports:

#define degreesToRadian(x) (M_PI * (x) / 180.0)

затем в методе viewWillAppear:

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}

если вы хотите, чтобы это было анимировано, то вы можете обернуть все это в блок анимации, например так:

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
[UIView commitAnimations];

Затем в контроллере портретного режима вы можете сделать обратное - проверить, находится ли он в настоящее время в альбомной ориентации, и, если это так, повернуть его обратно в книжный режим.

16
Bdebeez

Если вы хотите заставить его вращаться от портрета до пейзажа, вот код. Просто отметьте, что вам нужно отрегулировать центр вашего обзора. Я заметил, что мой не поместил вид в правильном месте. В остальном все работало отлично. Спасибо за чаевые.

if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){

        [UIView beginAnimations:@"View Flip" context:nil];
        [UIView setAnimationDuration:0.5f];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        self.view.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
        self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
        self.view.center = CGPointMake(160.0f, 240.0f);

        [UIView commitAnimations];
}
16
Michael Gaylord

Я копал и копал в поисках хорошего решения этой проблемы. Нашел этот пост в блоге, который делает трюк: удалите ваше внешнее представление из ключа UIWindow и добавьте его снова, система затем повторно запросит методы shouldAutorotateToInterfaceOrientation: из ваших контроллеров представления, принудительно применяя правильную ориентацию . Смотрите это: iphone заставляет uiview переориентироваться

7
nobre

Существует простой способ программно принудительно настроить iPhone на нужную ориентацию - использовать два из уже предоставленных ответов kdbdallas , Josh :

//will rotate status bar
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];


//will re-rotate view according to statusbar
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];

работает как шарм :)

Правка:

для iOS 6 мне нужно добавить эту функцию: (работает на модальном viewcontroller)

- (NSUInteger)supportedInterfaceOrientations
{
    return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight);
}
7
Guntis Treulands

У меня возникла проблема, когда у меня была UIViewController на экране, в UINavigationController, в альбомной ориентации. Когда следующий контроллер вида был помещен в поток, мне понадобилось устройство, чтобы вернуться в портретную ориентацию.

Я заметил, что метод shouldAutorotateToInterfaceOrientation: не вызывается, когда в стек помещается новый контроллер представления, но он вызывается, когда контроллер представления извлекается из стека .

Воспользовавшись этим, я использую этот фрагмент кода в одном из моих приложений:

- (void)selectHostingAtIndex:(int)hostingIndex {

    self.transitioning = YES;

    UIViewController *garbageController = [[[UIViewController alloc] init] autorelease];
    [self.navigationController pushViewController:garbageController animated:NO];
    [self.navigationController popViewControllerAnimated:NO];

    BBHostingController *hostingController = [[BBHostingController alloc] init];
    hostingController.hosting = [self.hostings objectAtIndex:hostingIndex];
    [self.navigationController pushViewController:hostingController animated:YES];
    [hostingController release];

    self.transitioning = NO;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    if (self.transitioning)
        return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
    else
        return YES;
}

По сути, создав пустой контроллер представления, поместив его в стек и немедленно вытолкнув его, можно вернуть интерфейс в исходное положение. После того, как контроллер отключен, я просто нажимаю на контроллер, который я намеревался нажать в первую очередь. Визуально это выглядит великолепно - пустой, произвольный контроллер представления никогда не будет виден пользователю.

7
Andrew Vilcsak

Ответ Джоша отлично работает для меня.

Однако я предпочитаю публиковать "ориентация изменилась, обновите пользовательский интерфейс". Когда это уведомление получено контроллером представления, он вызывает shouldAutorotateToInterfaceOrientation:, позволяя вам установить любую ориентацию, возвращая YES для нужной ориентации.

[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];

Единственная проблема заключается в том, что это вызывает переориентацию без / анимации. Вам нужно было обернуть эту строку между beginAnimations: и commitAnimations, чтобы добиться плавного перехода.

Надеюсь, это поможет.

5
Christian Schnorr

FWIW, вот моя реализация ручной установки ориентации (чтобы перейти в корневой контроллер вашего приложения, естественно): 

-(void)rotateInterfaceToOrientation:(UIDeviceOrientation)orientation{

    CGRect bounds = [[ UIScreen mainScreen ] bounds ];
    CGAffineTransform t;
    CGFloat r = 0;
    switch ( orientation ) {
        case UIDeviceOrientationLandscapeRight:
            r = -(M_PI / 2);
            break;
        case UIDeviceOrientationLandscapeLeft:
            r  = M_PI / 2;
            break;
    }
    if( r != 0 ){
        CGSize sz = bounds.size;
        bounds.size.width = sz.height;
        bounds.size.height = sz.width;
    }
    t = CGAffineTransformMakeRotation( r );

    UIApplication *application = [ UIApplication sharedApplication ];

    [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
    [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
    self.view.transform = t;
    self.view.bounds = bounds;
    [ UIView commitAnimations ];

    [ application setStatusBarOrientation: orientation animated: YES ];     
}

в сочетании со следующим методом UINavigationControllerDelegate (при условии, что вы используете UINavigationController):

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    // rotate interface, if we need to
    UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
    BOOL bViewControllerDoesSupportCurrentOrientation = [ viewController shouldAutorotateToInterfaceOrientation: orientation ];
    if( !bViewControllerDoesSupportCurrentOrientation ){
        [ self rotateInterfaceToOrientation: UIDeviceOrientationPortrait ];
    }
}

Это обеспечивает поворот корневого представления в зависимости от того, поддерживает ли входящая UIViewController текущую ориентацию устройства. Наконец, вам нужно подключить rotateInterfaceToOrientation к фактическим изменениям ориентации устройства, чтобы имитировать стандартную функциональность iOS. Добавьте этот обработчик событий в тот же корневой контроллер вида:

-(void)onUIDeviceOrientationDidChangeNotification:(NSNotification*)notification{
    UIViewController *tvc = self.rootNavigationController.topViewController;
    UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
    // only switch if we need to (seem to get multiple notifications on device)
    if( orientation != [[ UIApplication sharedApplication ] statusBarOrientation ] ){
        if( [ tvc shouldAutorotateToInterfaceOrientation: orientation ] ){
            [ self rotateInterfaceToOrientation: orientation ];
        }
    }
}

Наконец, зарегистрируйтесь для получения уведомлений UIDeviceOrientationDidChangeNotification в init или loadview следующим образом:

[[ NSNotificationCenter defaultCenter ] addObserver: self
                                           selector: @selector(onUIDeviceOrientationDidChangeNotification:)
                                               name: UIDeviceOrientationDidChangeNotification
                                             object: nil ];
[[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
3
Henry Cooke

Это работает для меня (спасибо Генри Кук):

Цель для меня состояла в том, чтобы иметь дело только с изменениями ландшафтных ориентаций.

метод init:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(orientationChanged:)
                                             name:UIDeviceOrientationDidChangeNotification
                                           object:nil];

- (void)orientationChanged:(NSNotification *)notification {    
    //[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    CGRect bounds = [[ UIScreen mainScreen ] bounds ];
    CGAffineTransform t;
    CGFloat r = 0;
    switch ( orientation ) {
        case UIDeviceOrientationLandscapeRight:
            r = 0;
            NSLog(@"Right");
            break;
        case UIDeviceOrientationLandscapeLeft:
            r  = M_PI;
            NSLog(@"Left");
            break;
        default:return;
    }

    t = CGAffineTransformMakeRotation( r );

    UIApplication *application = [ UIApplication sharedApplication ];
    [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
    [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
    self.view.transform = t;
    self.view.bounds = bounds;
    [ UIView commitAnimations ];

    [ application setStatusBarOrientation: orientation animated: YES ];
}
2
user324168

У меня есть приложение, в котором я хотел бы поддерживать поворот устройства в определенных видах, но другие не имеют особого смысла в режиме «Пейзаж», поэтому, меняя представления, я хотел бы установить поворот в портретный режим.

Я понимаю, что вышеприведенный оригинальный пост в этой теме сейчас очень старый, но у меня была похожая проблема - т.е. все экраны в моем приложении только портретные, за исключением одного экрана, который пользователь может поворачивать между альбомным и портретным.

Это было достаточно просто, но, как и в других публикациях, я хотел, чтобы приложение автоматически возвращалось в портретную ориентацию независимо от текущей ориентации устройства при возврате к предыдущему экрану.

Решение, которое я реализовал, заключалось в том, чтобы скрыть панель навигации в ландшафтном режиме, а это означает, что пользователь может вернуться только к предыдущим экранам в портретном режиме. Поэтому все остальные экраны могут быть только в портретном режиме.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation {
    BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES;
    [self.navigationController setNavigationBarHidden:lHideNavBar animated:YES];
}

Это также имеет дополнительное преимущество для моего приложения в том, что в альбомном режиме доступно больше места на экране. Это полезно, потому что рассматриваемый экран используется для отображения PDF файлов.

Надеюсь это поможет.

1
stephencampbell

решение для iOS 6:

[[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{
    [[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil];
}];

Точный код зависит от приложения, а также от того, где вы его разместили (я использовал его в своем AppDelegate). Замените [[self window] rootViewController] на то, что вы используете. Я использовал UITabBarController.

1
Bo A

Я решил это довольно легко в конце. Я перепробовал все предложенные выше предложения, но все равно потерпел неудачу, так что это было мое решение:

В ViewController, который должен оставаться альбомным (левым или правым), я слушаю изменения ориентации:

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(didRotate:)
                                             name:UIDeviceOrientationDidChangeNotification object:nil];

Тогда в didRotate:

- (void) didRotate:(NSNotification *)notification
{   if (orientationa == UIDeviceOrientationPortrait) 
    {
        if (hasRotated == NO) 
        {
            NSLog(@"Rotating to portait");
            hasRotated = YES;
            [UIView beginAnimations: @"" context:nil];
            [UIView setAnimationDuration: 0];
            self.view.transform = CGAffineTransformIdentity;
            self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90));
            self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
            self.view.frame = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
            [UIView commitAnimations];

    }
}
else if (UIDeviceOrientationIsLandscape( orientationa))
{
    if (hasRotated) 
    {
        NSLog(@"Rotating to lands");
        hasRotated = NO;
        [UIView beginAnimations: @"" context:nil];
        [UIView setAnimationDuration: 0];
        self.view.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(0));
        self.view.bounds = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
        self.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
        [UIView commitAnimations];

    }
}

Имейте в виду любые Super Views/Subviews, которые используют авторазмер, так как view.bounds/frame сбрасывается явно ... 

Единственное предостережение этого метода для сохранения ландшафта вида - это внутреннее переключение анимации между ориентациями, которое должно произойти, когда было бы лучше, чтобы оно казалось без изменений. 

1
mrdavenz

Я нашел решение и написал что-то по-французски (но код на английском). Вот

Способ заключается в том, чтобы добавить контроллер в окно (контроллер должен обладать хорошей реализацией функции shouldRotate ....).

0
Vinzius