41

使用 xcode 中的界面构建器和一个 .xib 文件,在横向和纵向之间旋转时如何创建替代布局?

查看不同布局的图表 在此处输入图像描述

。绿色视图/区域将包含 3 个横向横向流动的项目,而纵向这 3 个项目将在绿色视图/区域内垂直流动。

4

8 回答 8

45

一种方法是在 .xib 文件中包含三个视图。第一个是没有子视图的 Viewcontroller 的普通视图。

然后根据需要创建纵向和横向视图。这三个都必须是根级视图(看一下截图)

在此处输入图像描述

在您的 Viewcontroller 中,创建 2 个 IBOutlets,一个用于纵向视图,一个用于横向视图,并将它们与界面构建器中的相应视图连接起来:

IBOutlet UIView *_portraitView;
IBOutlet UIView *_landscapeView;
UIView *_currentView;

需要第三个视图_currentView来跟踪当前正在显示这些视图中的哪一个。然后像这样创建一个新函数:

-(void)setUpViewForOrientation:(UIInterfaceOrientation)orientation
{
    [_currentView removeFromSuperview];
    if(UIInterfaceOrientationIsLandscape(orientation))
    {
        [self.view addSubview:_landscapeView];
        _currentView = _landscapeView;
    }
    else
    {
        [self.view addSubview:_portraitView];
        _currentView = _portraitView;
    }
}

你需要从两个不同的地方调用这个函数,首先是初始化:

-(void)viewDidLoad
{
    [super viewDidLoad];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    [self setUpViewForOrientation:interfaceOrientation];
}

第二个是方向变化:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self setUpViewForOrientation:toInterfaceOrientation];
}

希望对你有帮助!

于 2013-09-12T14:36:16.130 回答
14

没有自动的方法来支持它,因为它违反了 Apple 的设计。您应该有一个支持两种方向的 ViewController。

但是如果你真的想这样做,你需要在旋转事件上重新加载 xib 并加载不同的 nib 文件。

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

- (NSString*) nibNameForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    NSString *postfix = (UIInterfaceOrientationIsLandscape(interfaceOrientation)) ? @"portrait" : @"landscape";
    return [NSString stringWithFormat:@"%@-%@", NSStringFromClass([self class]), postfix];
}

然后创建两个以“风景”和“肖像”为后缀的 nib 文件。

于 2013-09-10T11:52:40.287 回答
8

我喜欢@MeXx 的解决方案,但它有在内存中保留两个不同视图层次结构的开销。此外,如果任何子视图的状态(例如颜色)发生变化,则需要在切换层次结构时对其进行映射。

另一种解决方案可能是使用自动布局并为每个方向交换每个子视图的约束。如果您在两个方向的子视图之间有 1:1 映射,这将最有效。

可以理解的是,您希望使用 IB 直观地定义每个方向的布局。根据我的计划,您必须按照@MeXx 的规定进行操作,然后创建一种机制来在加载笔尖后存储两组约束(awakeFromNib)并在布局上重新应用正确的设置(viewWillLayoutSubviews)。刮取并存储其约束后,您可以丢弃辅助视图层次结构。(由于约束是特定于视图的,您可能会创建新的约束以应用于实际的子视图)。

抱歉,我没有任何代码。这只是现阶段的计划。

最后一点 - 使用 xib 比使用情节提要更容易,因为在情节提要中描述位于视图控制器主视图之外的视图是很痛苦的(这是可取的,因为否则它是要编辑的 PITA)。布莱克!

于 2013-09-13T20:48:16.133 回答
3

是的,先生,您当然可以....

我以前做的是在设备旋转时手动给框架

每当设备旋转时,- (void)viewWillLayoutSubviews都会被调用

例如 -UIButton *button在你的UIView,

- (void)viewWillLayoutSubviews
   {
       if (UIDeviceOrientationIsLandscape([self.view interfaceOrientation]))
       {
         //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];

       }
       else
       {
         //In potrait
          //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];


       }

   }

这样你就可以随意放置了。谢谢

请看看这些图片

XIB我想要在两个屏幕上显示的 xCode UIView 的第一张图片

在此处输入图像描述

现在我也想在风景中看这个视图所以我去编辑我的-(void)viewWillLayoutSubviews

在此处输入图像描述

现在的结果是这样的 模拟器中的人像屏幕的第一张图片

在此处输入图像描述

这是在风景中

在此处输入图像描述

于 2013-09-16T17:43:43.360 回答
2

使用情节提要 [ios7] 的解决方案。在主视图控制器内部,有可以包含标签栏控制器的容器视图。在标签栏控制器内部,有 2 个标签。一个选项卡用于纵向控制器,另一个选项卡用于横向模式控制器。

主视图控制器实现了这些功能:

#define INTERFACE_ORIENTATION() ([[UIApplication sharedApplication]statusBarOrientation])

@interface MainViewController ()
@property(nonatomic,weak) UITabBarController *embeddedTabBarController;
@end

@implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"embedContainer"])
    {
        self.embeddedTabBarController = segue.destinationViewController;
        [self.embeddedTabBarController.tabBar setHidden:YES];
    }
}

- (void) adaptToOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
    {
        [self.embeddedTabBarController setSelectedIndex:1];
    }
    else
    {
        [self.embeddedTabBarController setSelectedIndex:0];
    }
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    [self adaptToOrientation:interfaceOrientation];    
}

- (void) viewWillAppear:(BOOL)animated
{
    [self adaptToOrientation:INTERFACE_ORIENTATION()];
}

故事板中的 segue 链接应命名为“embedContainer”。

确保容器视图可调整大小以满足父视图,无论是横向还是纵向。

注意在运行时同一控制器的 2 个实例同时存在的事实。

故事板


更新: 备用横向视图控制器的 Apple 文档

于 2014-01-30T23:58:48.423 回答
2

我只想添加一个新答案来反映 ios8 和 XCode 6 中即将推出的新功能。

在最新的新软件中,Apple 引入了尺寸等级,使情节提要能够根据您使用的屏幕尺寸和方向智能地进行调整。尽管我建议您查看上面的文档或 WWDC 2014 会话使用 UIKit 构建自适应应用程序,但我会尝试解释一下。

确保启用尺寸等级, .

您会注意到您的故事板文件现在是方形的。您可以在此处设置基本界面。界面将针对所有启用的设备和方向智能调整大小。

在屏幕底部,您将看到一个显示 yAny xAny 的按钮。通过单击它,您可以只修改一个尺寸类别,例如横向和纵向。

我建议您阅读上面的文档,但我希望这对您有所帮助,因为它只使用一个故事板。

于 2014-08-29T13:18:44.003 回答
1

您可以使用库GPOrientation。这是链接

于 2013-09-13T10:48:14.950 回答
1

以编程方式调整大小

我有一个应用程序,我的情况完全相同。在我的 NIB 中,我只有纵向布局。另一种布局以编程方式执行。以编程方式指定几个大小并不难,因此无需维护两个 NIB。

请注意,通过设置帧以编程方式执行视图更改将产生动画(或者可以使用动画轻松制作)。这将为用户提供有关哪个组件移动到哪个位置的有价值的反馈。如果你只是加载第二个视图,你将失去这个优势,从 UI 的角度来看,我相信加载一个替代视图不是一个好的解决方案。

使用两个 ViewController

如果您喜欢使用两个 ViewController,那么 Apple 在其开发人员文档中描述了如何执行此操作,即您可以在此处找到问题的答案:RespondingtoDeviceOrientationChanges

如果您喜欢以编程方式执行此操作,您可以在方法中添加重新定位代码didRotateFromInterfaceOrientation

例子

我将该方法添加到我的 ViewController 中:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self adjustLayoutToOrientation];
}

然后实施了布局的调整。这是一个示例(代码特定于我的应用程序,但您可以读取超级视图的大小并从中计算子视图的帧。

- (void)adjustLayoutToOrientation
{
    UIInterfaceOrientation toInterfaceOrientation = self.interfaceOrientation;
    bool isIPhone = !([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);

    [self adjustViewHeight];
    [self adjustSubviewsToFrame: [mailTemplateBody.superview.superview frame]];

    CGRect pickerViewWrapperFrame = pickerViewWrapper.frame;
    if(isIPhone) {
        pickerViewWrapperFrame.origin.x = 0;
        pickerViewWrapperFrame.size.height = keyboardHeight;
        pickerViewWrapperFrame.origin.y = [self view].frame.size.height-pickerViewWrapperFrame.size.height;
        pickerViewWrapperFrame.size.width = [self view].frame.size.width;
    }
    else {
        pickerViewWrapperFrame = pickerView.frame;
    }

// MORE ADJUSTMENTS HERE

}
于 2013-09-16T20:44:30.370 回答