【问题标题】:How to Remove and Adding Constraints when we change simulator orientation(like portrait to landscape) in ios当我们在 ios 中更改模拟器方向(如纵向到横向)时如何删除和添加约束
【发布时间】:2015-11-19 15:44:47
【问题描述】:

嗨,我是自动布局的新手,在我的项目中,我使用自动布局以编程方式添加了两个 UIView,并且我添加了两个 UIButton,它们是 Next 和 Back 按钮,当我单击 Next 按钮时,我将 MyFirst UIView 推到第二个UIView 使用 UIView 动画,当我单击后退按钮时,我从第二个 UIView 推回到第一个 UIView ok 一切都很好,基于我的代码

但是这里我的主要问题是当我将模拟器中的第一个 UIView 方向从纵向更改为横向时,然后第二个 UIView 与我的第一个 UIView 重叠,我知道这一定是约束问题,我的意思是我必须删除并添加每个约束当我们为此更改方向时,我尝试过下面的代码,但这不起作用,请帮助我,为此我已经尝试了很长时间,但没有人使用约束说出正确的答案

当我们在纵向模式下运行程序时,屏幕就像 image1 一样,当我更改景观模式时,第二个 UIview 重叠在我的第一个 UIview 上,如下面的第二个图像,这是我的主要问题

我的代码:-

  #import "SubViewController.h"


@interface SubViewController (){

    UIButton * GoNext;
    UIButton * GoBack;

    NSArray * FHorizental;
    NSArray * FVertical;

    NSArray * SHorizental;
    NSArray * SVertical;
}

@end

@implementation SubViewController

@synthesize MyFisrtView,MySecondView;

- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"How are you");

    [self callAutolayouts];

    MyFisrtView.hidden = NO;
    MySecondView.hidden = YES;
}

-(void)callAutolayouts{

    NSLog(@"Hi");

    MyFisrtView = [[UIView alloc] init];
    MyFisrtView.backgroundColor = [UIColor orangeColor];
    MyFisrtView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:MyFisrtView];

    MySecondView = [[UIView alloc] init];
    MySecondView.backgroundColor = [UIColor lightGrayColor];
    MySecondView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:MySecondView];

    //Applying autolayouts for  MyFirstView and MySecondView

    NSDictionary * HeaderDictionary = NSDictionaryOfVariableBindings(MyFisrtView,MySecondView);

    //Appliying Autolayouts for FirstView

    FHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"H:|-0-[MyFisrtView]-0-|"]
                                                         options:0
                                                         metrics:nil
                                                           views:HeaderDictionary];


    FVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[MyFisrtView]-0-|"]
                                                        options:0
                                                        metrics:nil
                                                          views:HeaderDictionary];

    [self.view addConstraints:FHorizental];
    [self.view addConstraints:FVertical];

    //Appliying Autolayouts for SEcondView

    SHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"H:|-0-[MySecondView]-0-|"]
                                                         options:0
                                                         metrics:nil
                                                           views:HeaderDictionary];


    SVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[MySecondView]-0-|"]
                                                        options:0
                                                        metrics:nil
                                                          views:HeaderDictionary];

    [self.view addConstraints:SHorizental];
    [self.view addConstraints:SVertical];


    GoNext = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    GoNext.frame = CGRectMake(50, 50, 100, 18);
    [GoNext setTitle:@"Next" forState:UIControlStateNormal];
    [GoNext addTarget:self action:@selector(GoNext:) forControlEvents:UIControlEventTouchUpInside];
    GoNext.backgroundColor = [UIColor lightGrayColor];
    [MyFisrtView addSubview:GoNext];

    GoBack = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    GoBack.frame = CGRectMake(50, 50, 100, 18);
    [GoBack setTitle:@"Back" forState:UIControlStateNormal];
    [GoBack addTarget:self action:@selector(GoBack:) forControlEvents:UIControlEventTouchUpInside];
    GoBack.backgroundColor = [UIColor orangeColor];
    [MySecondView addSubview:GoBack];
}

-(void)GoNext:(id)sender{

    MySecondView.hidden = NO;

    MySecondView.frame=CGRectMake(248, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position

    [UIView animateWithDuration:0.5f
                          delay:0.0f
                        options:UIViewAnimationOptionBeginFromCurrentState
                     animations:^{

                         [self.view removeConstraints:self.view.constraints];
                         [self callAutolayouts];
                         [self.view setNeedsLayout];

                         [MySecondView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];   // final visible position

                     }
                     completion:nil];
}

-(void)GoBack:(id)sender{

    MySecondView.frame=CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position

    [UIView animateWithDuration:0.5f
                          delay:0.0f
                        options:UIViewAnimationOptionBeginFromCurrentState
                     animations:^{

                         [self.view removeConstraints:self.view.constraints];
                         [self callAutolayouts];
                         [self.view setNeedsLayout];

                         [MySecondView setFrame:CGRectMake(MyFisrtView.frame.size.width, 0, self.view.frame.size.width, self.view.frame.size.height)];   // final visible position
                     }
                     completion:nil];
}

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{

    if(toInterfaceOrientation == UIInterfaceOrientationPortrait){

        NSLog(@"portrait");

         [self.view removeConstraints:self.view.constraints];

         [self callAutolayouts];

         [self.view setNeedsLayout];

    }

    else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {

        NSLog(@"LandscapeRight");

        [self.view removeConstraints:self.view.constraints];

        [self callAutolayouts];

        [self.view setNeedsLayout];
    }

    else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {

        NSLog(@"LandscapeLeft");

        [self.view removeConstraints:self.view.constraints];

        [self callAutolayouts];

        [self.view setNeedsLayout];
    }
}

@end

图像1:-

图像2:-

【问题讨论】:

  • 我也面临同样的问题 AbhiRam 我喜欢这个问题
  • @AbhiRam 我的理解是您只想在纵向模式下显示一个视图(与下一个一起查看),而在横向模式下您想同时显示两个视图(与下一个和后退一起查看)对吗?
  • 有两种方法可以摆脱这个问题,使用基本布局 (600,600) 制作您的界面,并使用 IBOutlet 进行高度约束,并将此高度约束连接到文件所有者并将高度约束设为零到视图,当你想显示你的第二个和第一个视图时,你可以改变你的高度约束值,比如假设 IBOutlet NSLayoutConstraint *heightFirstView; [heightFirstView setConstant:0.0f] 最初,当您想要重叠您的第一个视图时,请在适当的委托方法中更改此约束值
  • Jaggen 我的意思是当我单击“下一步”按钮时,我想将第一个 UIView 移动到第二个 UIView,当“返回”按钮我想将第二个 UIView 推回第一个 UIView 时,当我改变时从纵向到景观的方向然后第二个 UIView 重叠在我的第一个 UIView 上,这是这里的主要问题
  • Latika Tiwari 我在这里以编程方式使用自动布局,所以请告诉我我做错了什么

标签: ios objective-c ios-autolayout


【解决方案1】:

首先,您必须删除约束,然后像您在问题中所做的那样添加 agin。

【讨论】:

  • 我对发布此问题的约束条件了解不多
  • 文档的哪一部分你不明白?您阅读了文档,对吗?而“你必须删除约束然后再添加”正是答案。
  • 好吧 gnasher729 我已经更新了我的代码添加意味着在方向改变时删除和添加约束但是这不起作用你能解释一下我做错了什么
【解决方案2】:

您可以按照有关自动布局的教程进行操作,以便对它有所了解。这里是a link

另一种选择是使用springs and struts

我希望这能回答你的问题。

【讨论】:

    【解决方案3】:

    根据您下面的 cmets,我正在更新我的答案,但保留下面的原始答案,因为它可能会对您或其他人有所帮助。

    那么你的问题不是真正的自动布局,你真正的问题是架构设计问题。每个出现在屏幕上的视图都应该有一个不同的视图控制器运行它。听起来您正在绘制一个两倍于设备宽度的视图,一半(我猜是左半边)上有下一个按钮,而另一半则包括后退按钮。一旦您的应用程序在不同尺寸的屏幕上运行,除非您检查屏幕尺寸,否则它将中断。即使这样,当设备旋转时它也会中断(如您所见)。

    您需要创建两个 不同 视图控制器子类,一个用于运行“下一个”视图,一个用于“后退”视图,然后您将永远不会在屏幕上看到这两个视图,因为设备已旋转.

    真正很难获得你想要的效果的方法是像你迄今为止尝试过的那样在代码中完成这一切。如果有一些奇怪的原因,您只是必须 编写所有代码而不是使用启动项目时包含的情节提要,您将需要查看 UIView 的动画方法(特别是查看在+ (void)transitionFromView: toView: duration: options: completion: 的文档中,如果您想要翻转或卷页之类的东西)。如果您想在两个视图之间滑动,您应该真正查看UINavigationController,因为它会为您完成所有这些。另一个选项是在按下 Next 按钮时创建第二个视图控制器,该控制器将创建第二个视图,并将其框架设置为离屏。然后将视图的框架动画到屏幕上的位置。 Back 必须再次将 Back 视图的框架从屏幕上移开并移除该视图。完成所有这些工作需要大量代码,这意味着要阅读大量文档,我强烈建议使用情节提要来解决这个问题。

    如果您想要简单 方式,请使用情节提要并拖出第二个视图控制器。在您希望它们所在的任何位置为每个场景添加一个按钮,并根据需要设置文本。控制从 Next 按钮拖动到第二个场景,然后选择您想要的过渡类型。将以下代码添加到使用 Next 按钮运行视图的视图控制器中

    @IBAction func exitToViewController (sender: UIStoryboardSegue){ }

    控制从“后退”按钮拖动到Exit 图标(场景顶部一直向右,它是红色的,带有一个白框和一个向右的箭头)选择exitToViewController。选择其中一个场景,然后选择 Editor -> Resolve Auto Layout Issues -> Reset To Suggested Constraints(所有视图下的第二个)。只需五分钟即可完成,效果非常好。

    如果您在 Storyboard 路线中提到的任何任务上需要帮助,请考虑在 Xcode 上查找初学者级别的教程(Coursera.org 上有一个非常好的概述课程,它不是很长,但涵盖了许多称为,Foundations of Objective-C App Development 如果您不关心经过验证的证书,它是免费的。

    原答案

    是否有理由必须以编程方式构建 UI? 在 IB 中构建它会让您的生活更加更轻松,因为您可以构建 UI 的基本版本,然后为特定大小类创建覆盖(查看 IB 底部的 w Any h Any 并更改选择紧凑高度)。一旦进入紧凑高度,就可以对 UI 元素的约束和可见性进行任何修改。系统应该从那里处理好事情(您可能必须根据方向的需要手动隐藏和显示正确的 UI 元素)。 在 IB 中构建时,其中一个助手编辑器是预览版。您可以选择各种设备并在横向和纵向之间更改方向,这将允许您实时查看实际运行时视图将要做什么,而不是更改代码然后再次运行应用程序。如果需要,您可以为每台设备添加一个,它们都可以有自己的方向,让您可以同时以横向和纵向查看 UI。

    如果确实必须在代码中完成,并且您必须从 UI 中实际删除未使用的元素而不是隐藏它们,那么您将不得不为横向设置所有约束每次设备旋转到该方向,纵向方向也是如此。最好的办法是在动画块中进行约束更改,而不是手动更新帧,这将导致视图动画到位(您可以同时进行任何 alpha 调整以淡入和淡出元素)。

    【讨论】:

    • 所以你说从 UI 中删除未使用的元素而不是隐藏它们你必须在每次设备旋转到该方向时设置横向方向的所有约束,你能解释一下吗?请以编程方式进行,因为我对自动布局很陌生
    • 根据您的附加 cmets,听起来您实际上想要做的是让应用程序在屏幕上以 view A 开头,无论手机是横向还是纵向。该视图应该有一个显示“下一步”的按钮。点击下一步按钮应该会调出view B(同样是横向或纵向)。视图 B 应该有一个显示“返回”的按钮,点击它应该切换回视图 A。您遇到的问题是,当您旋转到横向时,您会看到两个视图。这是一个正确的评估吗?
    • 你好,我想通过编程来满足我的要求,我不应该在这里使用两个不能解决我的要求的视图控制器
    • 您至少需要制作两个视图,每个视图一个控制器更好,并且是iOS的标准。也许您需要更清楚地说明要求,或者要求您将两个视图放在一个视图中的人需要重新考虑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-02
    • 2018-03-13
    • 2014-06-21
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    相关资源
    最近更新 更多