仅使用一个.xib文件的纵向和横向的替代iOS布局

时间:2013-09-03 15:02:39

标签: ios xcode interface-builder xib screen-orientation

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

查看不同布局的图表 enter image description here

N.b 即可。绿色视图/区域将包含横向流动的3个项目,在纵向中,这3个项目将在绿色视图/区域内垂直流动。

8 个答案:

答案 0 :(得分:44)

执行此操作的方法是在.xib文件中包含三个视图。第一个是ViewController的普通视图,没有子视图。

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

enter image description here

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

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];
}

希望能帮到你!

答案 1 :(得分: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];
}

然后使用“landscape”和“portrait”创建两个后缀固定的nib文件。

答案 2 :(得分:8)

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

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

可以理解,您希望使用IB直观地定义每个方向的布局。根据我的计划,您必须执行@MeXx规定的操作,但是一旦加载了nib(awakeFromNib),然后创建一个机制来存储两组约束,并在布局上重新应用正确的集合({{1 }})。抓取并存储其约束后,您可以丢弃辅助视图层次结构。 (由于约束是特定于视图的,因此您可能会创建新的约束以应用于实际的子视图。)

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

最后的注意事项 - 使用xib比使用故事板更容易,因为在故事板中描述居住在视图控制器主视图之外的视图是很痛苦的(这是理想的,因为否则它是PITA编辑的)。 Blach!

答案 3 :(得分: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)];


       }

   }

通过这种方式,您可以随意放置它。感谢

请查看这些图片

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

enter image description here

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

enter image description here

现在结果就像是模拟器中的第一张potrait Screen图像

enter image description here

,这是在风景

enter image description here

答案 4 :(得分: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个实例同时存在。

Storyboard


更新: Apple docs for alternate landscape view controller

答案 5 :(得分:2)

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

在最新的新软件中,Apple推出了size classes,它使故事板能够根据您使用的屏幕尺寸和方向进行智能调整。虽然我建议您查看上面的文档或WWDC 2014会话building adaptive apps with UIKit,但我会试着解释一下。

确保尺寸等级为enabled,

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

在屏幕底部,您会看到一个按yAny xAny的按钮。通过单击此按钮,您只能修改一个尺寸类,例如横向和纵向。

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

答案 6 :(得分:1)

您可以使用库GPOrientation。这是link

答案 7 :(得分:1)

以编程方式调整大小

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

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

使用两个ViewControllers

如果您想使用两个ViewControllers,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

}