我正在使用 IBOutletCollections 对类似 UI 元素的多个实例进行分组。特别是我将一些 UIButtons(类似于问答游戏中的蜂鸣器)和一组 UILabels(显示分数)分组。我想确保按钮正上方的标签会更新分数。我认为通过索引访问它们是最容易的。不幸的是,即使我以相同的顺序添加它们,它们也不总是具有相同的索引。Interface Builder 中有没有办法设置正确的顺序。
10 回答
编辑:一些评论者声称更新版本的 XcodeIBOutletCollections
按连接的顺序返回。其他人声称这种方法在故事板中对他们不起作用。我自己没有对此进行测试,但如果您愿意依赖未记录的行为,那么您可能会发现我在下面提出的显式排序不再需要。
不幸的是,似乎没有任何方法可以控制IBOutletCollection
IB 中的顺序,因此您需要在数组加载后根据视图的某些属性对其进行排序。您可以根据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)];
}];
}
我使用 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];
}
不确定这何时发生了确切的变化,但至少从 Xcode 4.2 开始,这似乎不再是问题。IBOutletCollections 现在保留了在 Interface Builder 中添加视图的顺序。
更新:
我做了一个测试项目来验证是否是这种情况:IBOutletCollectionTest
据我所知,不是。
作为一种解决方法,您可以按顺序为每个人分配一个标签。有按钮范围 100、101、102 等和标签 200、201、202 等。然后将 100 添加到按钮的标签以获取其对应标签的标签。然后,您可以使用viewForTag:
.
或者,您可以将相应的对象分组到它们自己的UIView
中,这样每个视图只有一个按钮和一个标签。
我发现 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 代码中按顺序显示,这会有所帮助。
@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
}
}
}
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 来访问对象。这节省了每次我需要访问元素时必须遍历和比较标签的开销。
我需要对 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;
}
}
这是我创建的一个扩展,Array<UIView>
用于排序tag
,例如,在使用 w/ IBOutletCollection
s 时很有用。
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
}
}
}
我使用@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!]
}
}