2

我需要为纵向和横向使用不同的 xib 文件。我没有使用自动布局,但我使用的是 iOS6。(如果你关心为什么,请参阅我之前的问题。)

我正在关注 Adam 对这个问题的回答,该问题使用 amergin 的 initWithNib 名称技巧进行了修改,并根据我自己的 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 的样子。(我给它们上色了,所以你可以看到它们是不同的。)

在此处输入图像描述在此处输入图像描述

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

在此处输入图像描述

当我旋转到纵向时,它也是正确的

在此处输入图像描述

但是当我旋转回横向时,xib 被替换,但视图关闭了 90 度。

在此处输入图像描述

这里有什么问题?

4

3 回答 3

1

我一直在走的道路可能与去年保罗塞尚所做的相同。不确定他是否尝试过这个,但我解决了最初的问题(在这个问题中说明),只是让我的根控制器成为导航控制器而不是我的视图控制器类。由于我使用的是“空项目”模板和 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 关于支持多方向的最简单方法的评论?当应用程序处于横向时,如何加载自定义 NIB?我第一次阅读它们时不应该忽略它们:

我将视图控制器添加到导航控制器并呈现它,使其按预期工作

编辑:在示例代码中添加了额外的行以隐藏导航栏,因为大多数关注此问题的人都不希望这样做。

于 2014-09-12T21:12:08.893 回答
0

我在这里回答我自己的问题,粘贴在我的 iOS 博客上的完整文章http://www.notthepainter.com/topologically-challenged-ui/

我有一个朋友帮助我,他在一个带有 IBOutlets 的 xib 文件中使用了 2 个视图,用于纵向和横向视图,并且他在设备旋转时在它们之间切换。完美,对吧?好吧,不,当您在 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];
}

如果您阅读了我之前关于容器视图控制器的博客文章,那么 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 检查 xib 是否存在于我们的包中。如果是,我们返回 xib 名称。如果没有,我们再次尝试将设备名称放入 xib 名称中。如果存在则返回它,如果不存在则断言失败。Portrait 类似,但按照惯例,我们不会将“-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 文件中不小心混入 Model 数据。而我正是这样做的。当我运行上面的代码时,它运行良好,我可以整天旋转它,并且 UI 在两个方向上都是正确的。但是,当我在锻炼计时器运行时旋转设备时,您认为会发生什么?是的,它们都被删除了,UI 重置为初始状态。这根本不是我想要的!

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

然后我就完成了。

于 2013-07-23T16:51:15.133 回答
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];

}
于 2013-07-03T13:18:53.230 回答