在iOS6下旋转多个xib的问题

时间:2013-07-02 20:35:54

标签: ios ios6 autorotate

我需要为肖像和风景使用不同的xib文件。我没有使用自动布局,但我使用的是iOS6。 (如果您关心原因,请参阅my previous question。)

我正在关注Adam对使用amergin的initWithNib名称技巧修改的this question的回答,并根据我自己的iPhone / iPad需求进行了修改。这是我的代码:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{    
    [[NSBundle mainBundle] loadNibNamed:[self xibNameForDeviceAndRotation:toInterfaceOrientation]
                                  owner: self
                                options: nil];
    [self viewDidLoad];
}

- (NSString *) xibNameForDeviceAndRotation:(UIInterfaceOrientation)toInterfaceOrientation
{
    NSString *xibName ;
    NSString *deviceName ;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        deviceName = @"iPad";
    } else {
        deviceName = @"iPhone";
    }

    if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )
    {
        xibName = [NSString stringWithFormat:@"%@-Landscape", NSStringFromClass([self class])];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@-Landscape", NSStringFromClass([self class]), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }

    } else {
        xibName = [NSString stringWithFormat:@"%@", NSStringFromClass([self class])];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@", NSStringFromClass([self class]), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }
    }
}

当然我在做:

- (BOOL) shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}

在我的视图控制器中:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown);
}

在我的代表中。

我有两个可能相关的问题。首先,简单的一个。我不颠倒旋转。我已经在iPad和iPhone的xcode中打开了所有正确的位。这可能是一个单独的问题,也可能是我问题的核心。

真正的问题是,当我旋转到横向模式时,我的xib会被替换,但视图会偏离90度。

这是我的2 xib的样子。 (我把它们弄得很漂亮,所以你可以看出它们是不同的。)

enter image description hereenter image description here

你可以看到我运行它(最初是在横向模式下)时横向xib是否正确。

enter image description here

当我旋转到肖像时它也是正确的

enter image description here

但是当我旋转回横向时,xib会被替换,但视图会偏离90度。

enter image description here

这里有什么问题?

3 个答案:

答案 0 :(得分:1)

我一直在追随Paul Cezanne去年那样的道路。不确定他是否尝试过这个,但我解决了原始问题(在这个问题中说明),只是让我的根控制器成为导航控制器而不是我的视图控制器类。因为我正在使用"空项目"模板和XIB文件,这意味着改变正常:

self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;

在AppDelegate.m中,改为:

self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navigationController.navigationBar.hidden = YES;
self.window.rootViewController = navigationController;

也就是说,我刚创建了一个通用的UINavigationController并将其设置为根视图控制器。

我不确定这是否会导致其他问题,并且可能有一种方法可以找出(可能你需要源代码)UINavigationController做的事情UIViewController没有做到。可以像在正确的位置一样多的setNeedsLayout类型的调用一样简单。如果我弄清楚,我会为未来的读者编辑这个答案。

归功于Sakti对Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape?的评论,我第一次阅读时不应忽略这些评论:

  

我将视图控制器添加到导航控制器并显示它   这使它按预期工作

编辑:为示例代码添加了额外的行来隐藏导航栏,因为大多数人都不希望这样做。

答案 1 :(得分:0)

我就是这样做的,它适用于iOS 5 +:

   - (void)viewDidLoad {
   [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(checkBagRotation)
                                             name:UIApplicationDidChangeStatusBarOrientationNotification
                                           object:nil];
  [self checkBagRotation];
 }

- (void)checkBagRotation {
    orientation = [UIApplication sharedApplication].statusBarOrientation;
    if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
        [[NSBundle mainBundle] loadNibNamed:@"Controller-landscape"
                                      owner:self
                                    options:nil];
   } else {
        [[NSBundle mainBundle] loadNibNamed:@"Controller-portrait"
                                      owner:self
                                    options:nil];

}

答案 2 :(得分:0)

我在这里回答了我自己的问题,在我的iOS博客http://www.notthepainter.com/topologically-challenged-ui/

上写完整篇文章。

我有一个朋友帮助我,他在一个xib文件中使用了2个视图,其中IBOutlets用于纵向和横向视图,他在它们之间切换设备旋转。完美,对吗?好吧,不,当你在XIB中有2个视图时,你无法将你的IBOutlets连接到这两个地方。我让它在视觉上有效,但我的控件只能在一个方向上工作。

我最终提出了使用定向主视图控制器的想法,该控制器在设备旋转时加载容器视图控制器。这工作得很好。让我们看看代码:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    if (_timerViewController) {
        [_timerViewController.view removeFromSuperview];
        [_timerViewController willMoveToParentViewController:nil];
        [_timerViewController removeFromParentViewController];
        self.timerViewController = nil;
    }

    self.timerViewController = [[XTMViewController alloc] initWithNibName:
          [self xibNameForDeviceAndRotation:interfaceOrientation withClass:[XTMViewController class]]
                                             bundle:nil];

    // use bounds not frame since frame doesn't take the status bar into account
    _timerViewController.view.frame = _timerViewController.view.bounds = self.view.bounds;

    [self addChildViewController:_timerViewController];
    [_timerViewController didMoveToParentViewController:self];
    [self.view addSubview: _timerViewController.view];
}

如果您阅读我之前关于Container View Controllers的博客条目,那么addChildViewController和didMoveToParentViewController应该很熟悉。尽管如此,有两件事需要注意。我将首先处理第二个,我设置子视图控制器的框架和边界,而不是框架。这是考虑到状态栏。

注意调用xibNameForDeviceAndRotation从其xib文件加载视图控制器。让我们看一下代码:

- (NSString *) xibNameForDeviceAndRotation:(UIInterfaceOrientation)toInterfaceOrientation withClass:(Class) class;
{
    NSString *xibName ;
    NSString *deviceName ;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        deviceName = @"iPad";
    } else {
        deviceName = @"iPhone";
    }

    if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )  {
        xibName = [NSString stringWithFormat:@"%@-Landscape", NSStringFromClass(class)];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@-Landscape", NSStringFromClass(class), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }
    } else {
        xibName = [NSString stringWithFormat:@"%@", NSStringFromClass(class)];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@", NSStringFromClass(class), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }
    }
    return nil;
}

这里有很多事情要发生。我们来看看吧。我首先确定你是在iPhone还是iPad上。 xib文件的名称中将包含iPhone或iPad。接下来我们检查一下我们是否处于横向模式。如果是,我们使用类反射通过NSStringFromClass从类名构建测试字符串。接下来,我们使用pathForResource来检查我们的bundle中是否存在xib。如果是,我们返回xib名称。如果没有,我们再次尝试将设备名称放入xib名称。如果它存在则返回它,如果不存在则声明失败。肖像是相似的,除非按照惯例,我们不将“-Portrait”放入xib名称。

这段代码非常有用且非常通用,我将把它放在我的EnkiUtils开源项目中。

由于这是iOS6,我们需要输入iOS6旋转样板代码:

- (BOOL) shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}

奇怪的是,我们还需要在iPad上手动调用willAnimateRotationToInterfaceOrientation。 iPhone会自动获得willAnimateRotationToInterfaceOrientation,但iPad却没有。

- (void) viewDidAppear:(BOOL)animated
{
    // iPad's don't send a willAnimate on launch...
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        [self willAnimateRotationToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] duration:0];
    }
}

那么,我们完成了吗?令人尴尬的没有。你看,当我编写XTMViewController类时,我打破了模型 - 视图 - 控制器设计模式!这很容易做到,Apple已经通过将View和Controller放在同一个类中来帮助我们。并且很容易在VC的.h文件中不小心混合模型数据。我完成了那个。当我运行上面的代码时,它工作得很好,我可以整天旋转它,两个方向的UI都是正确的。但是当我在运动计时器运行时旋转设备时,您认为发生了什么?是的,他们都被删除了,UI重置为初始状态。这根本不是我想要的!

我创建了一个XTMUser类来保存所有的计时数据,我将所有NSTimers放入XTMOrientationMasterViewController类,然后我制作了一个协议,以便XTMOrientationMasterViewController可以响应XTMViewController类中的UI点击。

然后我就完成了。