草庐IT

ios - 如何使约束(自动布局)以编程方式工作

coder 2024-01-19 原文

这是我的第一个 iOS 项目,因此我在约束和自动布局方面遇到了困难。

这是我正在尝试做的。我想将 subview 添加到 UIScrollView。这是我以编程方式进行的,因为 subview 的数量不是静态的。当我只添加一个 subview (在代码或 XIB 中)或通过 XIB 添加多个 subview 时,我可以使自动布局和约束完美地工作。但是我无法让它在代码中使用动态数量的 subview 。

创建了多个 subview 实例,但由于约束不起作用,它们都在彼此之上。我希望他们在每场比赛中都排好队,这就是为什么我试图让约束发挥作用。 (目前我只是将 subview 的数量设置为 10,但这只是暂时的)

这是我的一些代码(从按下按钮开始):

-(IBAction) buttonTapped:(id)sender {

UIScrollView *matchScrollView = (UIScrollView *) [self.view viewWithTag:1];
NSMutableArray *matchViewsArray = [[NSMutableArray alloc] init];

for (int i = 0; i <= 9; i++) {
    MatchView *newMatchView = [[NSBundle mainBundle] loadNibNamed:@"MatchView" owner:self options:nil].firstObject;
    [matchViewsArray addObject:newMatchView];
}

for (int i = 0; i < [matchViewsArray count]; i++) {
    MatchView *newMatchView = matchViewsArray[i];

//I kept some IBOutlets out of the code example here. They work fine with a single subview. or even multiple subviews as long as you don't mind they are all on top of each other due to constraints not working xD
    [newMatchView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [matchScrollView addSubview:matchViewsArray[i]];

    [matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                              attribute:NSLayoutAttributeLeading
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:matchScrollView
                                                              attribute:NSLayoutAttributeLeading
                                                             multiplier:1.0
                                                               constant:8.0]];

    [matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                attribute:NSLayoutAttributeTrailing
                                                                relatedBy:NSLayoutRelationEqual
                                                                   toItem:matchScrollView
                                                                attribute:NSLayoutAttributeTrailing
                                                               multiplier:1.0
                                                                 constant:8.0]];

    //If this is not the first object, then the top of the object contraints with the bottom of the previous object
    if (i > 0) {
        // I think the fault may lie here. Do i need to remove the previous constraint, or will it be overwritten? This did not work however, did i remove it wrong?

        //Making sure it only tries to call removeconstraint: once and once only, as the constraint is only put on the else statement which only executes on the very first object. Attempt to fix the code
        if (i == 1) {
        [matchScrollView removeConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i - 1]
                                                                    attribute:NSLayoutAttributeTop
                                                                    relatedBy:NSLayoutRelationEqual
                                                                        toItem:matchScrollView
                                                                    attribute:NSLayoutAttributeTop
                                                                    multiplier:1.0
                                                                    constant:8.0]];
        }

        [matchViewsArray[i - 1] addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                    attribute:NSLayoutAttributeTop
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:matchViewsArray[i - 1]
                                                                    attribute:NSLayoutAttributeBottom
                                                                   multiplier:1.0
                                                                     constant:8.0]];
    }

    //The top of the first object constraints with the top of the matchScrollView
    else {
        [matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                      attribute:NSLayoutAttributeTop
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:matchScrollView
                                                                      attribute:NSLayoutAttributeTop
                                                                     multiplier:1.0
                                                                       constant:8.0]];
    }

    //If this is the last object, then the bottom of the object constraints with the bottom of matchScrollView
    if ( i == [matchViewsArray count] - 1) {
        //tried removing the constraint created by else statement first before adding a new constraint
        [matchViewsArray[i] removeConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i - 1]
                                                                           attribute:NSLayoutAttributeBottom
                                                                           relatedBy:NSLayoutRelationEqual
                                                                              toItem:matchViewsArray[i]
                                                                           attribute:NSLayoutAttributeTop
                                                                          multiplier:1.0
                                                                            constant:8.0]];

        [matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                    attribute:NSLayoutAttributeBottom
                                                                    relatedBy:NSLayoutRelationGreaterThanOrEqual
                                                                       toItem:matchScrollView
                                                                    attribute:NSLayoutAttributeBottom
                                                                   multiplier:1.0
                                                                     constant:8.0]];
    }
    //else the bottom of the object contraints with the top of the next object
    else {
        [matchViewsArray[i + 1] addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                    attribute:NSLayoutAttributeBottom
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:matchViewsArray[i + 1]
                                                                    attribute:NSLayoutAttributeTop
                                                                   multiplier:1.0
                                                                     constant:8.0]];
    }

    [matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
                                                                attribute:NSLayoutAttributeCenterX
                                                                relatedBy:NSLayoutRelationEqual
                                                                   toItem:matchScrollView
                                                                attribute:NSLayoutAttributeCenterX
                                                               multiplier:1.0
                                                                 constant:0.0]];
}

我一直收到“以 NSException 类型的未捕获异常终止”。我尝试在添加新约束之前删除旧约束,但这没有帮助。除非我删除错误?

如果我删除所有的 if 和 else 语句,只创建一个 subview ,那么约束就可以完美地工作。如果我创建多个 subview ,那么应用程序不会崩溃,但所有 subview 都具有相同的约束并且彼此重叠。因此,通过添加 if 和 else 语句并使用 matchViewsArray[i - 1],我尝试为多个 subview 设置约束。

此外,即使这段代码有效,真的没有更简单的方法来代替循环中的所有这些 if 和 else 语句吗?

如前所述,这是我的第一个 iOS 项目,因此可能有更高效、更好的方法来实现我想要的行为。

如有任何帮助,我们将不胜感激。

最佳答案

不幸的是,我没有制作 iOS 应用程序的许可证,也无法按原样对其进行测试,但这个 OSX 代码片段可能会有所帮助:

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    NSView *contentView = [window contentView];

    NSMutableArray *buttons = [[NSMutableArray alloc] init];

    for (int i = 0; i < 10; i++) {
        NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
        [button setTranslatesAutoresizingMaskIntoConstraints:NO];
        [button setTitle:[NSString stringWithFormat:@"Button %d", (int)pow(10, i)]];
        [button setBezelStyle:NSRoundedBezelStyle];
        [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
        [button sizeToFit];

        [buttons addObject:button];
        [contentView addSubview:button];
    }
    int count = (int)[buttons count];

    // 1. one below other
    // 2. all equal width

    for (int i = 1; i < count; i++) {
        NSView *view1 = buttons[i-1];
        NSView *view2 = buttons[i];
        NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);

        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[view1]-[view2]"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view1(==view2)]"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
    }

    // 3. first -> top

    if (count > 0) {
        NSView *view = [buttons firstObject];
        NSDictionary *views = NSDictionaryOfVariableBindings(view);

        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
    }

    // 4. last -> bottom

    if (count > 0) {
        NSView *view = [buttons lastObject];
        NSDictionary *views = NSDictionaryOfVariableBindings(view);

        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[view]-|"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
    }

    // 5. left <- all -> right

    for (int i = 0; i < count; i++) {
        NSView *view = buttons[i];
        NSDictionary *views = NSDictionaryOfVariableBindings(view);

        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-(>=20)-|"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
    }
}

请注意,您不能在不使用“视觉格式语言”的情况下指定默认的 8/20 间距,因为 constraintsWithVisualFormat:... 会产生特殊的约束(好吧,里面有特殊的私有(private)标志)自动- 在特定情况下将 8 调整为 6/4/根据 Apple 的 HIG 调整大小。 (您可以尝试在 IB 中放置控件并发现默认值并不总是 8,但在运行时它突然是!)。

编辑:此外,您可以始终使用简单的#define 或子例程来隐藏 [[[content-constraint-with-contraint-visual-constraint-with-constraints-ponies]]]梯子。

edit2: 重构阶段:)

关于ios - 如何使约束(自动布局)以编程方式工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29661991/

有关ios - 如何使约束(自动布局)以编程方式工作的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  10. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

随机推荐