xcode IBOutletCollection 在 Interface Builder 中设置排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6527762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 21:23:08  来源:igfitidea点击:

IBOutletCollection set ordering in Interface Builder

objective-ciosxcodeinterface-builderiboutlet

提问by gebirgsb?rbel

I am using IBOutletCollections to group several Instances of similar UI Elements. In particular I group a number of UIButtons (which are similar to buzzers in a quiz game) and a group of UILabels (which display the score). I want to make sure that the label directly over the button updates the score. I figured that it is easiest to access them by index. Unfortunately even if I add them in the same order, they do not always have the same indexes. Is there a way in Interface Builder to set the correct ordering.

我正在使用 IBOutletCollections 对几个类似 UI 元素的实例进行分组。特别是我将一些 UIButtons(类似于问答游戏中的蜂鸣器)和一组 UILabels(显示分数)分组。我想确保按钮正上方的标签更新分数。我认为通过索引访问它们是最容易的。不幸的是,即使我以相同的顺序添加它们,它们也不总是具有相同的索引。Interface Builder 中有没有办法设置正确的顺序。

回答by cduhn

EDIT: Several commenters have claimed that more recent versions of Xcode return IBOutletCollectionsin the order the connections are made. Others have claimed that this approach didn't work for them in storyboards. I haven't tested this myself, but if you're willing to rely on undocumented behavior, then you may find that the explicit sorting I've proposed below is no longer necessary.

编辑:一些评论者声称更新的 Xcode 版本IBOutletCollections按照连接的顺序返回。其他人声称这种方法在故事板中对他们不起作用。我自己还没有对此进行过测试,但是如果您愿意依赖未记录的行为,那么您可能会发现我在下面提出的显式排序不再必要。



Unfortunately there doesn't seem to be any way to control the order of an IBOutletCollectionin IB, so you'll need to sort the array after it's been loaded based on some property of the views. You could sort the views based on their tagproperty, but manually setting tags in IB can be rather tedious.

不幸的是,似乎没有任何方法可以控制IBOutletCollectionIB 中的顺序,因此您需要在根据视图的某些属性加载数组后对其进行排序。您可以根据视图的tag属性对视图进行排序,但在 IB 中手动设置标签可能相当乏味。

Fortunately we tend to lay out our views in the order we want to access them, so it's often sufficient to sort the array based on x or y position like this:

幸运的是,我们倾向于按照我们想要访问的顺序排列我们的视图,所以根据 x 或 y 位置对数组进行排序通常就足够了,如下所示:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Order the labels based on their y position
    self.labelsArray = [self.labelsArray sortedArrayUsingComparator:^NSComparisonResult(UILabel *label1, UILabel *label2) {
        CGFloat label1Top = CGRectGetMinY(label1.frame);
        CGFloat label2Top = CGRectGetMinY(label2.frame);

        return [@(label1Top) compare:@(label2Top)];
    }];
}

回答by Ivan Dossev

I ran with cduhn's answer and made this NSArray category. If now xcode really preserves the design-time order this code is not really needed, but if you find yourself having to create/recreate large collections in IB and don't want to worry about messing up this could help (at run time). Also a note: most likely the order in which the objects were added to the collection had something to do with the "Object ID" you find in the Identity Inspector tab, which can get sporadic as you edit the interface and introduce new objects to the collection at a later time.

我用 cduhn 的答案运行并创建了这个 NSArray 类别。如果现在 xcode 真的保留了设计时的顺序,那么这个代码并不是真正需要的,但是如果您发现自己必须在 IB 中创建/重新创建大型集合并且不想担心弄乱这可能会有所帮助(在运行时)。另请注意:很可能将对象添加到集合中的顺序与您在身份检查器选项卡中找到的“对象 ID”有关,当您编辑界面并将新对象引入稍后收集。

.h

。H

@interface NSArray (sortBy)
- (NSArray*) sortByObjectTag;
- (NSArray*) sortByUIViewOriginX;
- (NSArray*) sortByUIViewOriginY;
@end

.m

.m

@implementation NSArray (sortBy)

- (NSArray*) sortByObjectTag
{
    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
        return(
            ([objA tag] < [objB tag]) ? NSOrderedAscending  :
            ([objA tag] > [objB tag]) ? NSOrderedDescending :
            NSOrderedSame);
    }];
}

- (NSArray*) sortByUIViewOriginX
{
    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
        return(
            ([objA frame].origin.x < [objB frame].origin.x) ? NSOrderedAscending  :
            ([objA frame].origin.x > [objB frame].origin.x) ? NSOrderedDescending :
            NSOrderedSame);
    }];
}

- (NSArray*) sortByUIViewOriginY
{
    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
        return(
            ([objA frame].origin.y < [objB frame].origin.y) ? NSOrderedAscending  :
            ([objA frame].origin.y > [objB frame].origin.y) ? NSOrderedDescending :
            NSOrderedSame);
    }];
}

@end

Then include the header file as you chose to name it and the code can be:

然后包含您选择命名的头文件,代码可以是:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Order the labels based on their y position
    self.labelsArray = [self.labelsArray sortByUIViewOriginY];
}

回答by Nick Lockwood

Not sure when this changed exactly, but as of Xcode 4.2 at least, this no longer seems to be a problem. IBOutletCollections now preserve the order in which the views were added in Interface Builder.

不确定这什么时候完全改变了,但至少从 Xcode 4.2 开始,这似乎不再是一个问题。IBOutletCollections 现在保留在 Interface Builder 中添加视图的顺序。

UPDATE:

更新:

I made a test project to verify that this is the case: IBOutletCollectionTest

我做了一个测试项目来验证是这样的:IBOutletCollectionTest

回答by Jim

Not as far as I am aware.

据我所知没有。

As a workaround, you could assign each of them a tag, sequentially. Have the buttons range 100, 101, 102, etc. and the labels 200, 201, 202, etc. Then add 100 to the button's tag to get its corresponding label's tag. You can then get the label by using viewForTag:.

作为一种解决方法,您可以按顺序为它们每个分配一个标签。设置按钮范围为100、101、102等,标签为200、201、202等,然后在按钮的标签上加上100,得到对应标签的标签。然后,您可以使用 获取标签viewForTag:

Alternatively, you could group the corresponding objects into their own UIView, so you only have one button and one label per view.

或者,您可以将相应的对象分组到它们自己的 中UIView,这样每个视图只有一个按钮和一个标签。

回答by massimobio

I found that Xcode sorts the collection alphabetically using the ID of the connection. If you open the version editor on your nib file you can easily edit the id's (making sure they are unique otherwise Xcode will crash).

我发现 Xcode 使用连接的 ID 按字母顺序对集合进行排序。如果您在 nib 文件上打开版本编辑器,您可以轻松编辑 ID(确保它们是唯一的,否则 Xcode 会崩溃)。

<outletCollection property="characterKeys" destination="QFa-Hp-9dk" id="aaa-0g-pwu"/>
<outletCollection property="characterKeys" destination="ahU-9i-wYh" id="aab-EL-hVT"/>
<outletCollection property="characterKeys" destination="Kkl-0x-mFt" id="aac-0c-Ot1"/>
<outletCollection property="characterKeys" destination="Neo-PS-Fel" id="aad-bK-O6z"/>
<outletCollection property="characterKeys" destination="AYG-dm-klF" id="aae-Qq-bam"/>
<outletCollection property="characterKeys" destination="Blz-fZ-cMU" id="aaf-lU-g7V"/>
<outletCollection property="characterKeys" destination="JCi-Hs-8Cx" id="aag-zq-6hK"/>
<outletCollection property="characterKeys" destination="DzW-qz-gFo" id="aah-yJ-wbx"/>

It helps if you first order your object manually in the Document Outline of IB so they show up in sequence in the the xml code.

如果您首先在 IB 的文档大纲中手动订购您的对象,那么它们会按顺序显示在 xml 代码中,这会有所帮助。

回答by Scott Gardner

Here's an extension I created on Array<UIView>to sort by tag, e.g., useful when working w/ IBOutletCollections.

这是我创建的一个扩展,Array<UIView>用于排序tag,例如,在使用IBOutletCollections时很有用。

extension Array where Element: UIView {

  /**
   Sorts an array of `UIView`s or subclasses by `tag`. For example, this is useful when working with `IBOutletCollection`s, whose order of elements can be changed when manipulating the view objects in Interface Builder. Just tag your views in Interface Builder and then call this method on your `IBOutletCollection`s in `viewDidLoad()`.
   - author: Scott Gardner
   - seealso:
   * [Source on GitHub](http://bit.ly/SortUIViewsInPlaceByTag)
   */
  mutating func sortUIViewsInPlaceByTag() {
    sortInPlace { (left: Element, right: Element) in
      left.tag < right.tag
    }
  }

}

回答by Gallaugher

The extension proposed by @scott-gardner is great & solves problems such as a collection of [UIButtons] not showing in the expected order. The below code is simply updated for Swift 5. Thanks really goes to Scott for this! extension Array where Element: UIView {

@scott-gardner 提出的扩展很棒,并且解决了诸如 [UIButtons] 集合未按预期顺序显示的问题。以下代码仅针对 Swift 5 进行了更新。非常感谢 Scott!扩展数组其中元素:UIView {

  /**
   Sorts an array of `UIView`s or subclasses by `tag`. For example, this is useful when working with `IBOutletCollection`s, whose order of elements can be changed when manipulating the view objects in Interface Builder. Just tag your views in Interface Builder and then call this method on your `IBOutletCollection`s in `viewDidLoad()`.
   - author: Scott Gardner
   - seealso:
   * [Source on GitHub](bit dot ly/SortUIViewsInPlaceByTag)
   */
  mutating func sortUIViewsInPlaceByTag() {
    sort { (left: Element, right: Element) in
      left.tag < right.tag
    }
  }

}

回答by anon_dev1234

It seems very random how IBOutletCollection is ordered. Maybe I am not understanding Nick Lockwood's methodology correctly - but I as well made a new project, added a bunch of UILabels, and connected them to a collection in the order they were added to the view.

IBOutletCollection 的排序方式似乎非常随机。也许我没有正确理解尼克洛克伍德的方法 - 但我也做了一个新项目,添加了一堆 UILabels,并按照它们被添加到视图中的顺序将它们连接到一个集合。

After logging, I got a random order. It was very frustrating.

登录后,我得到了一个随机订单。这非常令人沮丧。

My workaround was setting tags in IB and then sorting the collections like so:

我的解决方法是在 IB 中设置标签,然后像这样对集合进行排序:

[self setResultRow1:[self sortCollection: [self resultRow1]]];

Here, resultRow1 is an IBOutletCollection of about 7 labels, with tags set through IB. Here is the sort method:

这里,resultRow1 是一个大约有 7 个标签的 IBOutletCollection,标签通过 IB 设置。这是排序方法:

-(NSArray *)sortCollection:(NSArray *)toSort {
    NSArray *sortedArray;
    sortedArray = [toSort sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
        NSNumber *tag1 = [NSNumber numberWithInt:[(UILabel*)a tag]];
        NSNumber *tag2 = [NSNumber numberWithInt:[(UILabel*)b tag]];
        return [tag1 compare:tag2];
    }];

    return sortedArray;
}

Doing this, I can now access objects by using [resultRow1 objectAtIndex: i]or such. This saves overhead of having to iterate through and compare tags every time I need to access an element.

这样做,我现在可以通过使用[resultRow1 objectAtIndex: i]等来访问对象。这节省了每次我需要访问元素时必须遍历和比较标签的开销。

回答by David Robles

I needed this ordering for a collection of UITextField objects for setting where the "Next" button on the keyboard would lead to (field tabbing). This is going to be an international app so I wanted the language direction to be ambiguous.

我需要对 UITextField 对象集合进行此排序,以设置键盘上的“下一步”按钮将导致的位置(字段选项卡)。这将是一个国际应用程序,所以我希望语言方向不明确。

.h

。H

#import <Foundation/Foundation.h>

@interface NSArray (UIViewSort)

- (NSArray *)sortByUIViewOrigin;

@end

.m

.m

#import "NSArray+UIViewSort.h"

@implementation NSArray (UIViewSort)

- (NSArray *)sortByUIViewOrigin {
    NSLocaleLanguageDirection horizontalDirection = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
    NSLocaleLanguageDirection verticalDirection = [NSLocale lineDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
    UIView *window = [[UIApplication sharedApplication] delegate].window;
    return [self sortedArrayUsingComparator:^NSComparisonResult(id object1, id object2) {
        CGPoint viewOrigin1 = [(UIView *)object1 convertPoint:((UIView *)object1).frame.origin toView:window];
        CGPoint viewOrigin2 = [(UIView *)object2 convertPoint:((UIView *)object2).frame.origin toView:window];
        if (viewOrigin1.y < viewOrigin2.y) {
            return (verticalDirection == kCFLocaleLanguageDirectionLeftToRight) ? NSOrderedDescending : NSOrderedAscending;
        }
        else if (viewOrigin1.y > viewOrigin2.y) {
            return (verticalDirection == kCFLocaleLanguageDirectionLeftToRight) ? NSOrderedAscending : NSOrderedDescending;
        }
        else if (viewOrigin1.x < viewOrigin2.x) {
            return (horizontalDirection == kCFLocaleLanguageDirectionTopToBottom) ? NSOrderedDescending : NSOrderedAscending;
        }
        else if (viewOrigin1.x > viewOrigin2.x) {
            return (horizontalDirection == kCFLocaleLanguageDirectionTopToBottom) ? NSOrderedAscending : NSOrderedDescending;
        }
        else return NSOrderedSame;
    }];
}

@end

Usage (after layout)

用法(布局后)

- (void)viewDidAppear:(BOOL)animated {
    _availableTextFields = [_availableTextFields sortByUIViewOrigin];

    UITextField *previousField;
    for (UITextField *field in _availableTextFields) {
        if (previousField) {
            previousField.nextTextField = field;
        }
        previousField = field;
    }
}