当方向改变时重新定位控件

Posted

技术标签:

【中文标题】当方向改变时重新定位控件【英文标题】:Repositioning controls when the orientation changes 【发布时间】:2013-02-18 03:52:51 【问题描述】:

我知道自动布局可用于在方向更改时使大小和位置保持一致。方向改变时是否可以完全改变布局?

例如,请看下面一个简单的纵向模式登录屏幕的线框图。

现在,如果我旋转设备,我想完全重新定位控件。

这种事情可以通过使用自动布局来完成吗?如果没有,我该怎么办?

谢谢。

【问题讨论】:

是的,有可能。但是当方向改变时,您必须手动重置每个 UI 控件的框架 【参考方案1】:

在您的情况下,可以通过两种方法来实现,而不是重新构建每个组件,您可以将控件分组如下..

    父视图 -> 用户信息视图 -> 所有用户信息控件.. 通过这样做,您只需重新构建 用户信息视图 而不是所有控件..

    然后只剩下徽标和公司名称需要重新构建。如果您对控件进行分组,总共需要重新构建 3 个控件。.

    创建两个视图,一个用于纵向模式,另一个用于横向模式,只需在旋转时添加和删除。这是最快的方法,因为您不必通过繁琐的代码重新调整框架。

希望以上有所帮助。

【讨论】:

使用这两种方法时是否必须关闭自动布局? 我在问题中忘记提到的一件事是我正在使用情节提要。在情节提要场景中,您不能将 2 个视图插入 1 个 UIViewController,对吗?那我该怎么办? 您的解决方案 1 如何与故事板一起使用?如何准确分组?【参考方案2】:

你不能以不同的方式设置框架:-

-(void)setFramesForPotrait


// set frames here



-(void)setFramesForLandscaeMode


// set frames here



-(bool)shouldAutorotate.....

return Yes


-()willAutoRotate......

if(orientation = uiinterfaceOrientationPotrait)

[self setFramesForPotrait];

else

[self setFramesForLandscape];

【讨论】:

+1: 在一个 nib 中有两个视图的替代代码很好:D 这个评论是什么意思?? 查看@iphonic 的答案...数字 (2) 解释了我在其他地方看到的选项作为使用两个视图的替代方案...但是,对于纯代码方法,我更喜欢你的 如果您不使用布局约束,这很好。如果您使用它们,则根本不应该设置框架,而应该修改约束。 使用这个时是否必须关闭自动布局?【参考方案3】:

我遇到了同样的问题,这是我目前发现的问题。 我正在使用的方法是针对不同的屏幕尺寸或方向设置两组(可能多组)约束。所以我只是为肖像设计并将所有特定于肖像的约束连接到 IBOutletCollection: 然后将 InterfaceBuilder 中的 VC 方向切换为 Landscape 并为此方向添加所需的约束并连接到相应的插座:

然后在代码中我根据需要删除或添加约束:

- (void)viewWillLayoutSubviews

    [super viewWillLayoutSubviews];
    [self updateViewConstraints];


- (void)updateViewConstraints

    [super updateViewConstraints];
    if (self.view.frame.size.width >= kSize * 2 + kPadding * 3)
    
        [self.view removeConstraints:self.constraintsForPortrait];
        [self.view addConstraints:self.constraintsForLandscape];
     else
    
        [self.view removeConstraints:self.constraintsForLandscape];
        [self.view addConstraints:self.constraintsForPortrait];
    

您可以使用此代码获得类似的结果,基于方向而不是框架大小添加/删除约束:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self updateViewConstraints];

但请记住,有传言称 Apple 将发布具有不同屏幕纵横比的设备,并废弃 UIInterfaceOrientation 和旋转事件,这可能值得提前准备。 看看未来会发生什么。

这种方法的缺点是 Xcode 会抱怨设计模棱两可,而事实确实如此,因为 Xcode 不知道我们会在运行时消除模棱两可。为了解决这个问题,您可以将约束的优先级设置为对于您在此特定时刻未设计的所有其他方向是可选的(例如,如果您正在为纵向设计,则为横向选择约束)。

【讨论】:

P.S.:在更复杂的屏幕上,这变得如此血腥!【参考方案4】:

使用自动布局时,您可以覆盖 updateViewConstraints 以在方向更改时修改布局:

- (void)updateViewConstraints

  [super updateViewConstraints];
  //portrait
  if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation))

  
  //landscape
  else
    //
  

【讨论】:

我也读过,特别是“使用自动布局时”。我没有在我的布局中使用 xibs。框架如何知道我正在使用自动布局? @tote:据我所知,自动布局始终存在。您的 spring 和 struts 配置将被系统转换为自动布局。【参考方案5】:

可以通过如下所示的布局约束来完成。这不太有效,因为您的草图不太有效 - 与您在风景中实际看到的相比,您的风景视图太长了。您必须缩短登录文本字段以适应所有内容,但这应该让您了解它是如何完成的。 buttonWidth 约束显示了如何在视图的宽度和按钮的宽度之间建立负相关关系——也就是说,按钮的宽度在横向比纵向要小。我有几个 IBOutlets 到我在代码中引用的约束。如果您有兴趣,我可以为您描述它们,但我现在就把它扔在那里:

@implementation ViewController 
    IBOutlet NSLayoutConstraint *buttonWidth;
    IBOutlet NSLayoutConstraint *logoTop;
    IBOutlet NSLayoutConstraint *logoAlignToLabel;
    IBOutlet NSLayoutConstraint *logoSpaceToLabel;
    IBOutlet NSLayoutConstraint *coNameToButtonAlignment;
    IBOutlet UIButton *b;
    IBOutlet UIImageView *logo;
    IBOutlet UILabel *coName;
    NSLayoutConstraint *con2;
    NSArray *cons1;


- (void)viewDidLoad 
    [super viewDidLoad];
    [b removeConstraint:buttonWidth];
    buttonWidth = [NSLayoutConstraint constraintWithItem:b attribute:NSLayoutAttributeWidth relatedBy:0 toItem:self.view attribute:NSLayoutAttributeWidth multiplier:-.2193 constant:350];
    [self.view addConstraint:buttonWidth];
    [self.view layoutSubviews];


- (void)updateViewConstraints
    [super updateViewConstraints];
    if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
        if (con2 != nil) 
            [self.view removeConstraints:cons1];
            [self.view removeConstraint:con2];
            [self.view addConstraints:@[logoAlignToLabel,logoSpaceToLabel,logoTop,coNameToButtonAlignment]];
        
    else
        NSLog(@"Landscape");
        [self.view removeConstraints:@[logoAlignToLabel,logoSpaceToLabel,logoTop,coNameToButtonAlignment]];
        cons1 = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[logo]-4-[coName]" options:NSLayoutFormatAlignAllCenterY metrics:0 views:@@"logo":logo, @"coName":coName];
        con2 = [NSLayoutConstraint constraintWithItem:logo attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
        [self.view addConstraints:cons1];
        [self.view addConstraint:con2];
    

在轮换时,徽标和公司名称标签的约束被移除,新的约束被放置到位。我在 viewDidLoad 中添加的按钮的新约束会自动处理旋转,因此在旋转过程中我根本不需要调整它。

【讨论】:

其实,如果 logo 足够小,公司名称也足够短的话,这个效果很好。

以上是关于当方向改变时重新定位控件的主要内容,如果未能解决你的问题,请参考以下文章

当方向改变时,视图中何时重新加载资源?

WinForm开发中实现控件随窗体大小的改变而自动适应其改变

Windows Phone开发:处理屏幕方向的改变

根据屏幕大小重新排列 DOM 的最佳方法

在方向改变时重新计算 SnapKit 约束

重叠DialogFragment,在方向更改时以错误的顺序重新创建