IBOutletCollection устанавливает порядок в Интерфейсном Разработчике
Я использую IBOutletCollections для группировки нескольких экземпляров похожих элементов пользовательского интерфейса. В частности, я группирую несколько кнопок UIB (которые похожи на зуммеры в игре-викторине) и группу UILabels (которые отображают счет). Я хочу убедиться, что ярлык прямо над кнопкой обновляет счет. Я понял, что проще всего получить к ним доступ по индексу. К сожалению, даже если я добавлю их в том же порядке, они не всегда имеют одинаковые индексы. Есть ли способ в Интерфейсном Разработчике установить правильный порядок.
8 ответов
РЕДАКТИРОВАТЬ: Несколько комментаторов утверждают, что более поздние версии XCode возвращают IBOutletCollections
в порядке соединения сделаны. Другие утверждают, что этот подход не работает для них в раскадровках. Я не проверял это сам, но если вы хотите положиться на недокументированное поведение, то вы можете обнаружить, что предложенная ниже явная сортировка больше не нужна.
К сожалению, похоже, нет никакого способа контролировать порядок 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, и вы не хотите беспокоиться о том, чтобы испортить это, это может помочь (во время выполнения). Также примечание: скорее всего, порядок, в котором объекты были добавлены в коллекцию, как-то связан с "идентификатором объекта", который вы найдете на вкладке Identity Inspector, которая может быть случайной при редактировании интерфейса и введении новых объектов в Коллекция в более позднее время.
.час
@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 теперь сохраняет порядок, в котором представления были добавлены в Интерфейсном Разработчике.
ОБНОВИТЬ:
Я сделал тестовый проект, чтобы убедиться, что это так: IBOutletCollectionTest
Не настолько, насколько я знаю.
В качестве обходного пути вы можете назначить каждому из них тег последовательно. Установите для кнопок диапазон 100, 101, 102 и т. Д. И метки 200, 201, 202 и т. Д. Затем добавьте 100 к тегу кнопки, чтобы получить соответствующий тег метки. Вы можете получить ярлык с помощью viewForTag:
,
Кроме того, вы можете сгруппировать соответствующие объекты в свои UIView
Таким образом, у вас есть только одна кнопка и одна метка для просмотра.
Я обнаружил, что XCode сортирует коллекцию в алфавитном порядке, используя идентификатор соединения. Если вы откроете редактор версий в вашем nib-файле, вы можете легко редактировать идентификаторы (убедившись, что они уникальны, иначе 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. Большое спасибо Скотту за это! extension Массив, где Элемент: 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. Возможно, я неправильно понимаю методологию Ника Локвуда - но я также создал новый проект, добавил группу UILabels и соединил их с коллекцией в порядке их добавления в представление.
После входа я получил случайный заказ. Это было очень сложно.
Мой обходной путь - установка тегов в IB, а затем сортировка коллекций следующим образом:
[self setResultRow1:[self sortCollection: [self resultRow1]]];
Здесь resultRow1 - это коллекция IBOutletCollection из приблизительно 7 меток с тегами, установленными через 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;
}
Теперь я могу получить доступ к объектам с помощью [resultRow1 objectAtIndex: i]
или такой. Это избавляет от необходимости перебирать и сравнивать теги каждый раз, когда мне нужен доступ к элементу.
Вот расширение, которое я создал на Array<UIView>
сортировать по tag
Например, полезно при работе с 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!]
}
}
Мне нужно было упорядочить коллекцию объектов UITextField, чтобы указать, куда приведет кнопка "Далее" на клавиатуре (табуляция полей). Это будет международное приложение, поэтому я хотел, чтобы направление языка было неоднозначным.
.час
#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;
}
}