繁体   English   中英

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

[英]Alternative iOS layouts for portrait and landscape using just one .xib file

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

请参见不同布局的图表 在此输入图像描述

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

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

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

在此输入图像描述

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

希望对你有所帮助!

没有自动支持方式,因为它违反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文件。

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

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

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

对不起,我没有任何代码。 这只是现阶段的计划。

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

你肯定可以先生....

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

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

例如 - 在你的UIView取一个UIButton *button

- (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的第一张图片

在此输入图像描述

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

在此输入图像描述

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

在此输入图像描述

这是在风景中

在此输入图像描述

故事板解决方案[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文档

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

在最新的新软件中,Apple推出了尺寸等级 ,使故事板能够根据您使用的屏幕尺寸和方向进行智能调整。 虽然我建议您查看上面的文档或WWDC 2014会话使用UIKit构建自适应应用程序 ,但我会试着解释一下。

确保启用了大小类。

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

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

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

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

以编程方式调整大小

我有一个应用程序,我有完全相同的情况。 在我的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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM