46

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

4

10 回答 10

75

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


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

幸运的是,我们倾向于按照我们想要访问它们的顺序排列视图,因此根据 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)];
    }];
}
于 2011-07-20T05:01:59.003 回答
23

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

。H

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

.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

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

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Order the labels based on their y position
    self.labelsArray = [self.labelsArray sortByUIViewOriginY];
}
于 2012-04-14T16:39:20.063 回答
6

不确定这何时发生了确切的变化,但至少从 Xcode 4.2 开始,这似乎不再是问题。IBOutletCollections 现在保留了在 Interface Builder 中添加视图的顺序。

更新:

我做了一个测试项目来验证是否是这种情况:IBOutletCollectionTest

于 2012-03-01T00:48:57.407 回答
5

据我所知,不是。

作为一种解决方法,您可以按顺序为每个人分配一个标签。有按钮范围 100、101、102 等和标签 200、201、202 等。然后将 100 添加到按钮的标签以获取其对应标签的标签。然后,您可以使用viewForTag:.

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

于 2011-06-29T23:49:18.517 回答
5

我发现 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"/>

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

于 2013-08-13T18:23:46.700 回答
2

@scott-gardner 提出的扩展非常棒,解决了诸如 [UIButtons] 集合未按预期顺序显示的问题。下面的代码是为 Swift 5 简单更新的。非常感谢 Scott !扩展数组 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](bit dot ly/SortUIViewsInPlaceByTag)
   */
  mutating func sortUIViewsInPlaceByTag() {
    sort { (left: Element, right: Element) in
      left.tag < right.tag
    }
  }

}
于 2019-12-07T17:25:34.617 回答
1

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

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

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

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

这里,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;
}

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

于 2012-06-21T17:31:12.970 回答
1

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

。H

#import <Foundation/Foundation.h>

@interface NSArray (UIViewSort)

- (NSArray *)sortByUIViewOrigin;

@end

.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

用法(布局后)

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

    UITextField *previousField;
    for (UITextField *field in _availableTextFields) {
        if (previousField) {
            previousField.nextTextField = field;
        }
        previousField = field;
    }
}
于 2013-01-14T22:11:12.670 回答
1

这是我创建的一个扩展,Array<UIView>用于排序tag,例如,在使用 w/ 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
    }
  }

}
于 2015-10-21T18:42:21.167 回答
1

我使用@scott-gardner 提出的扩展来订购图像视图,以便使用点阵数字的单个 png 图像显示计数器。它在 Swift 5 中就像一个魅力。

self.dayDigits.sortUIViewsInPlaceByTag()

func updateDayDigits(countString: String){
        for i in 0...4 {
            dayDigits[i].image = offDigitImage
        }
        let length = countString.count - 1
        for i in 0...length {
            let char = Array(countString)[length-i]
            dayDigits[i].image = digitImages[char.wholeNumberValue!]
        }
    }
于 2020-11-15T16:09:22.233 回答