Iboutletcollection set ordering in Interface Builder

Używam IBOutletCollections do grupowania kilku instancji podobnych elementów interfejsu. W szczególności grupuję szereg Uibuttonów (które są podobne do buzzerów w quizie) i grupę UILabels (które wyświetlają wynik). Chcę się upewnić, że etykieta bezpośrednio nad przyciskiem aktualizuje wynik. Pomyślałem, że najłatwiej jest uzyskać do nich dostęp przez indeks. Niestety, nawet jeśli dodam je w tej samej kolejności, nie zawsze mają te same indeksy. Czy jest sposób w Interface Builder aby Ustaw prawidłową kolejność.

Author: gebirgsbärbel, 2011-06-30

8 answers

EDIT: kilku komentatorów twierdziło, że nowsze wersje Xcode zwracają IBOutletCollections w kolejności połączeń. Inni twierdzili, że to podejście nie działało dla nich w storyboardach. Sam tego nie testowałem, ale jeśli chcesz polegać na nieudokumentowanym zachowaniu, może się okazać, że wyraźne sortowanie, które zaproponowałem poniżej, nie jest już konieczne.


Niestety nie ma sposobu, aby kontrolować kolejność IBOutletCollection w IB, więc będziesz trzeba posortować tablicę po załadowaniu na podstawie jakiejś właściwości widoków. Można sortować widoki na podstawie ich właściwości tag, ale ręczne ustawianie tagów w IB może być dość żmudne.

Na szczęście mamy tendencję do układania naszych widoków w kolejności, w jakiej chcemy uzyskać do nich dostęp, więc często wystarczy posortować tablicę na podstawie pozycji x lub y w następujący sposób:

- (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)];
    }];
}
 74
Author: cduhn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-02 17:57:25

Pobiegłem z odpowiedzią cduhna i zrobiłem tę kategorię NSArray. Jeśli teraz xcode naprawdę zachowuje kolejność projektowania czas ten kod nie jest naprawdę potrzebne, ale jeśli znajdziesz się konieczności tworzenia / odtworzyć duże kolekcje w IB i nie chcesz się martwić o bałagan to może pomóc (w czasie wykonywania). Uwaga: najprawdopodobniej kolejność dodawania obiektów do kolekcji miała coś wspólnego z "ID obiektu", które można znaleźć na karcie Inspektor tożsamości, co może być sporadyczne podczas edycji interfejs i wprowadzanie nowych obiektów do kolekcji w późniejszym czasie.

.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

Następnie dołącz plik nagłówka, jak wybrałeś jego nazwę, A kod może być:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Order the labels based on their y position
    self.labelsArray = [self.labelsArray sortByUIViewOriginY];
}
 23
Author: Ivan Dossev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-14 20:21:01

Nie wiem, kiedy to się dokładnie zmieniło, ale od Xcode 4.2 przynajmniej, to już nie wydaje się być problemem. IBOutletCollections zachowuje teraz kolejność dodawania widoków w Kreatorze interfejsów.

UPDATE:

Zrobiłem projekt testowy, aby sprawdzić, czy tak jest: IBOutletCollectionTest

 6
Author: Nick Lockwood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 10:23:34

O ile mi wiadomo, nie.

Jako obejście można przypisać każdemu z nich znacznik, sekwencyjnie. Mają zakres przycisków 100, 101, 102 itp. oraz etykiety 200, 201, 202 itd. Następnie dodaj 100 do znacznika przycisku, aby uzyskać odpowiadający mu znacznik etykiety. Następnie możesz uzyskać etykietę za pomocą viewForTag:.

Alternatywnie, możesz grupować odpowiednie obiekty w ich własne UIView , więc masz tylko jeden przycisk i jedną etykietę na widok.

 5
Author: Jim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-29 23:49:18

Znalazłem, że Xcode sortuje kolekcję Alfabetycznie za pomocą ID połączenia. Jeśli otworzysz edytor wersji w pliku nib, możesz łatwo edytować identyfikatory (upewniając się, że są unikalne, w przeciwnym razie Xcode ulegnie awarii).

<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"/>

To pomaga, jeśli najpierw zamówić obiekt ręcznie w zarysie dokumentu IB tak, że pojawiają się w kolejności w kodzie xml.

 5
Author: massimobio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-13 18:23:46

Wydaje się bardzo przypadkowe, jak iboutletcollection jest uporządkowany. Może nie rozumiem metodologii Nicka Lockwooda poprawnie - ale zrobiłem również nowy projekt, dodałem kilka Uilabeli i podłączyłem je do kolekcji w kolejności, w jakiej zostały dodane do widoku.

Po zalogowaniu otrzymałem losowe zamówienie. To było bardzo frustrujące.

Moje obejście polegało na ustawianiu tagów w IB, a następnie sortowaniu kolekcji w następujący sposób:

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

Tutaj, resultRow1 jest IBOutletCollection of około 7 etykiet, z tagami ustawionymi przez IB. Oto metoda sortowania:

-(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;
}

Robiąc to, mogę teraz uzyskać dostęp do obiektów za pomocą [resultRow1 objectAtIndex: i] lub takich. Pozwala to zaoszczędzić na kosztach związanych z iteracją i porównywaniem tagów za każdym razem, gdy muszę uzyskać dostęp do elementu.

 1
Author: bgoers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-21 17:31:12

Potrzebowałem tego porządkowania dla kolekcji obiektów UITextField do Ustawienia, dokąd prowadzi przycisk" Dalej " na klawiaturze (tabbing pól). To będzie międzynarodowa aplikacja, więc chciałem, aby kierunek językowy był niejednoznaczny.

.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

Użycie (Po układzie)

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

    UITextField *previousField;
    for (UITextField *field in _availableTextFields) {
        if (previousField) {
            previousField.nextTextField = field;
        }
        previousField = field;
    }
}
 1
Author: David Robles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-14 22:11:12

Oto rozszerzenie, które stworzyłem na Array<UIView> do sortowania według tag, np. przydatne podczas pracy 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
    }
  }

}
 0
Author: Scott Gardner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 18:42:21