Problem z automatycznym dostosowywaniem rozmiaru ramki UICollectionViewCell contentView w komórce prototypu Storyboard (Xcode 6, iOS 8 SDK) występuje tylko podczas uruchamiania na iOS 7

158

Używam Xcode 6 Beta 3, iOS 8 SDK. Kompiluj docelowy iOS 7.0 przy użyciu języka Swift. Zapoznaj się z moim problemem krok po kroku ze zrzutami ekranu poniżej.

Mam UICollectionView w Storyboard. 1 Prototyp UICollectionViewCell, który zawiera 1 etykietę pośrodku (bez reguły autorezyzacji). Fioletowe tło miało oznaczać contentView, który jest generowany w czasie wykonywania przez Cell. Ten widok zostanie w końcu odpowiednio zmieniony na podstawie mojego UICollectionViewLayoutDelegate, ale nie na iOS 7. Zwróć uwagę, że używam Xcode 6 i problem występuje tylko w iOS 7.

Kiedy tworzę aplikację na iOS 8. Wszystko jest w porządku.

Uwaga: Fioletowy to contentView , niebieski to mój UIButton z zaokrąglonym rogiem.

http://i.stack.imgur.com/uDNDY.png

Jednak w systemie iOS 7 wszystkie widoki podrzędne w komórce nagle zmniejszają się do klatki (0,0,50,50) i nigdy nie są już zgodne z moją regułą autorezyzacji.

http://i.stack.imgur.com/lOZH9.png

Zakładam, że to błąd w iOS 8 SDK lub Swift, a może Xcode?


Aktualizacja 1: Ten problem nadal występuje w oficjalnym Xcode 6.0.1! Najlepszym rozwiązaniem jest to, co sugeruje KoCMoHaBTa poniżej, ustawiając ramkę w cellForItem komórki (musisz jednak podklasować swoją komórkę). Okazało się, że jest to niekompatybilność pomiędzy iOS 8 SDK i iOS 7 (sprawdź odpowiedź ecotax poniżej cytowaną z Apple).

Aktualizacja 2: Wklej ten kod na początku swojego cellForItem i wszystko powinno być w porządku:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/
thkeen
źródło
1
Dowiedziałem się, że ten problem nadal istnieje w Xcode 6 Beta 5. Czy ktoś też tego doświadczył?
thkeen
2
Zmagam się z tym teraz w moim projekcie. iOS 7 i iOS 8 zbudowane przy użyciu Xcode 5 wyglądają dobrze. iOS 8 zbudowany przy użyciu Xcode 6 beta 6 wygląda dobrze. iOS 7 zbudowany przy użyciu Xcode 6 beta 6 ma problem, który opisujesz. Korzystając z Reveal, widzę, że mój UICollectionViewCell ma odpowiedni rozmiar. Ale contentView komórki nie został zmieniony, mimo że jest to element nadrzędny, UICollectionViewCell ma włączoną opcję Autoresize Subviews. Rozmiar contentView jest ustawiony na to, co ma scenorys. Nie używam autoukładu w tym projekcie. Mój projekt jest całkowicie obiektywny-c.
Del Brown
2
Chciałem tylko dodać, że ten problem nadal istnieje od nasion GM Xcode 6 / iOS 8. Odpowiedź @ DanielPlamanna, aby wymusić contentViewzmianę rozmiaru komórki, działa dobrze, aby obejść problem. Wydaje mi się, że w iOS 8 Apple zmieniło coś w sposobie obsługi widoków zawartości komórek podczas tworzenia w Interface Builder (który i tak jest trochę czarną skrzynką). Ale fakt, że zmienia zachowanie podczas kierowania na iOS 7, jest z pewnością błędem.
Stuart
To samo dotyczy Xcode 6 GM, automatycznego układu i komórki opartej na końcówce. Naprawiam to, przypinając contentViewkrawędzie do krawędzi komórki.
sergiou87
3
Pobrałem xcode 6.1, ale nadal widzę ten sam problem w symulatorze.
Haitao Li

Odpowiedzi:

169

contentView jest uszkodzony. Można to również naprawić w awakeFromNib

CelC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
Igor Palaguta
źródło
3
Udało się bez self.contentView.frame = self.bounds; Myślisz, że tego potrzebuję? W każdym razie dzięki!
Michal Shatz
Witaj Michale, zgadzam się z tobą. Ale dla 100% pewności, jak będzie działać w następnych iOS-ach, myślę, że lepiej to dodać.
Igor Palaguta
5
Podoba mi się ta odpowiedź, ale musisz zadzwonić do [super awakeFromNib]; Zastanawiam się również nad dodaniem czeku dla iOS 7.1 i mniej, ponieważ nie jestem pewien, jak dodanie tych masek zmiany rozmiaru wpływa na domyślne zachowanie na iOS8.
GingerBreadMane
Jeśli nie używasz stalówek ani scenorysów, działa to również, jeśli umieścisz je w applyLayoutAttributes:
cetcet
To nie działa na mnie. Nie używam Autolayout !! Jakaś pomoc?
thatzprem
61

Napotkałem ten sam problem i poprosiłem Apple DTS o pomoc. Ich odpowiedź brzmiała:

W iOS 7 widoki zawartości komórek dopasowują się do siebie za pomocą automatycznych masek. W iOS 8 zostało to zmienione, komórki przestały używać automatycznych masek i zaczęły zmieniać rozmiar widoku zawartości w layoutSubviews. Jeśli końcówka jest zakodowana w systemie iOS 8, a następnie zdekodowana w systemie iOS 7, będziesz mieć widok zawartości bez maski automatycznego zmieniania rozmiaru i żadnych innych środków, za pomocą których można zmienić rozmiar. Więc jeśli kiedykolwiek zmienisz ramkę komórki, widok zawartości nie nastąpi.

Aplikacje wdrażane z powrotem do iOS 7 będą musiały obejść ten problem, zmieniając rozmiar samego widoku zawartości, dodając maski autorezowania lub dodając ograniczenia.

Myślę, że oznacza to, że nie jest to błąd w XCode 6, ale niekompatybilność między SDK iOS 8 a SDK iOS 7, która uderzy cię, jeśli zaktualizujesz Xcode 6, ponieważ automatycznie zacznie korzystać z SDK iOS 8.

Jak skomentowałem wcześniej, obejście Daniel Plamann opisało prace dla mnie. Te opisane przez Igora Palagutę i KoCMoHaBTa wyglądają jednak na prostsze i wydają się mieć sens, dając odpowiedź Apple DTS, więc spróbuję później.

ekotax
źródło
To interesujące, ale nadal mam nadzieję, że to naprawią. To zachowanie zostało wprowadzone tylko w Xcode 6 GM, który dodał obsługę iPhone'a 6. Działało dobrze we wcześniejszych wersjach beta. Nawet przywróciłem projekt do wcześniejszej wersji beta po tym, jak go zauważyłem i działał zgodnie z oczekiwaniami. Mam nadzieję, że wszyscy zgłaszają błędy Apple w tej sprawie.
arton
@arton Zgłosiłem błąd tego samego dnia, w którym poprosiłem DTS o pomoc. Został zamknięty jako duplikat 18312246. Nie wiem, jak bardzo to pomaga.
ekotax
Użyłem obejścia, zmieniając rozmiar ContentView i działa dla mnie. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

Napotkałem ten sam problem i mam nadzieję, że Apple naprawi to w następnej wersji Xcode. Tymczasem używam obejścia. W mojej UICollectionViewCellpodklasie właśnie layoutSubviewsnadpisałem i ręcznie zmieniłem rozmiar contentView na wypadek, gdyby rozmiar był inny niż collectionViewCellrozmiar.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}
Daniel Plamann
źródło
Tak, użyłem podobnej pracy, ale zrobiłem to w cellForItem / cellForRow, które również działa.
2014
1
To najlepsze rozwiązanie. Dodanie tego do cellForItem / cellForRow spowoduje dziwne artefakty, jeśli obracasz urządzenie i zmienia się rozmiar komórki.
spybart
Cześć! Mam problem z tym kodem. Po raz pierwszy wartość logiczna contentViewIsAutoresized będzie prawdziwa, jeśli zostanie załadowana z komórki scenorysu lub prototypu. Dopiero gdy wykonasz reloadData, sekunda będzie poprawna. Więc tak naprawdę nie musisz sprawdzać rozmiaru. Zamiast tego po prostu zrób: self.contentView.frame = self.bounds;
thkeen
Potwierdzone: powinniśmy to zrobić w cellForItem lub cellForRow, ponieważ layoutSubviews jest wywoływany dopiero po zwróceniu komórki. Cokolwiek zrobisz wcześniej, np. Rysowanie rzeczy, będzie źle obliczone.
thkeen
1
Hm, może lepiej jest umieścić [cell layoutIfNeeded];w cellForItem lub cellForRow?
wtorsi
38

Innym rozwiązaniem jest ustawienie rozmiaru contentView i automatycznej zmiany rozmiaru masek w -collectionView:cellForItemAtIndexPath:następujący sposób:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Działa to również z Auto Layout, ponieważ maski z automatyczną zmianą rozmiaru są tłumaczone na ograniczenia.

KoCMoHaBTa
źródło
2
Jest to doskonałe rozwiązanie, ponieważ działa z każdym typem komórki bez podklas i nie wymaga zmian w wielu miejscach, gdy używasz więcej niż jednego typu komórki w widoku kolekcji.
nacross
6

W Xcode 6.0.1 contentView dla UICollectionViewCell jest uszkodzony dla urządzeń iOS7. Można to również naprawić, dodając odpowiednie ograniczenia do UICollectionViewCell i jego contentView w metodach awakeFromNib lub init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
SerJ_G
źródło
Tylko to działa teraz, musisz to zrobić tylko dla IOS 8 i muszę to wywoływać po każdym usunięciu z kolejki! Ale to też nie jest idealne rozwiązanie, ponieważ wielokrotne
zaznaczanie
Najlepiej jest naprawdę używać autoukładu, z tym błędem oszczędzasz, jak na ironię, czas, używając zdarzenia autoukładu w prostym przypadku ..
Renetik
Maska na mnie nie działa; jednak ustawienie ograniczeń działa!
entropid
4

Nie będzie to działać poprawnie bez żadnego z innych wspomnianych obejść z powodu błędu w Xcode 6 GM w sposobie, w jaki Xcode kompiluje pliki xib do formatu nib. Chociaż nie mogę powiedzieć ze 100% pewnością, że jest to związane z Xcode i nie ma nic wspólnego ze środowiskiem wykonawczym, jestem bardzo pewien - oto jak mogę to pokazać:

  1. Build + Uruchom aplikację w Xcode 5.1.
  2. Przejdź do katalogu aplikacji symulatora i skopiuj skompilowany plik .nib dla xib, z którym masz problemy.
  3. Build + Uruchom aplikację w Xcode 6 GM.
  4. Zatrzymaj aplikację.
  5. Zastąp plik .nib w folderze symulatora nowo zbudowanej aplikacji plikiem .nib utworzonym za pomocą Xcode 5.1
  6. Uruchom ponownie aplikację z symulatora, NIE z Xcode.
  7. Twoja komórka załadowana z tego pliku .nib powinna działać zgodnie z oczekiwaniami.

Mam nadzieję, że każdy, kto czyta to pytanie, złoży radar w Apple. Jest to OGROMNY problem i wymaga rozwiązania przed ostatecznym wydaniem Xcode.

Edycja: W świetle posta ecotax chciałem tylko zaktualizować to, aby powiedzieć, że teraz potwierdzono różnice w zachowaniu między budowaniem w iOS 8 a iOS 7, ale nie jest to błąd. Mój hack naprawił problem, ponieważ tworzenie na iOS 7 dodało maskę autorezowania do widoku zawartości potrzebnego do tego, aby ta działała, której Apple już nie dodaje.

Acey
źródło
Nie jestem pewien, czy legalnie mogą wydać inny kod xCode niż wersja GM
Mabedan
Haha, czy pójdą do więzienia? = P Ale z całą powagą, na pewno mogą. Mieli wiele wersji GM dla Mavericks, jeśli nie pamiętasz. Nie jest to zbyt częste, ale może się zdarzyć.
Acey
4

Odpowiedzi w tym poście, nigdy nie zrozumiałem, dlaczego to działa.

Po pierwsze, istnieją dwie „zasady”:

  1. W przypadku widoków utworzonych programowo (np. [UIView new]) Właściwość translatesAutoresizingMaskIntoConstraintsjest ustawiona naYES
  2. Widoki utworzone w narzędziu do tworzenia interfejsów z włączonym układem automatycznym będą miały właściwość translatesAutoresizingMaskIntoConstraintsustawioną naNO

Wydaje się, że druga zasada nie ma zastosowania do widoków najwyższego poziomu, dla których nie zdefiniowano ograniczeń. (Np. Widok treści)

Patrząc na komórkę Storyboard, zwróć uwagę, że komórka nie jest contentViewodsłonięta. Nie „kontrolujemy” contentView, Apple to robi.

Zagłęb się w kod źródłowy scenorysu i zobacz, jak contentViewzdefiniowana jest komórka:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Teraz podglądy komórki (zwróć uwagę na translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

contentViewNie ma to translatesAutoresizingMaskIntoConstraintsustawienie NO. Ponadto brakuje definicji układu, być może z powodu tego, co powiedział @ecotax .

Jeśli spojrzymy na contentView, to ma maskę autorezyzacji, ale nie ma jej definicji: <autoresizingMask key="autoresizingMask"/>

Więc są dwa wnioski:

  1. contentView translatesAutoresizingMaskIntoConstraintsjest ustawiony na YES.
  2. contentView brakuje definicji układu.

To prowadzi nas do dwóch rozwiązań, o których mówiliśmy.

Możesz ustawić autorezalizację masek ręcznie w awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Lub można ustawić contentView translatesAutoresizingMaskIntoConstraintsaby NOw awakeFromNibi definiować ograniczenia w - (void)updateConstraints.

kgaidis
źródło
4

To jest wersja Swift odpowiedzi @ Igor, która została zaakceptowana i dziękuję za miłą odpowiedź.

Najpierw przejdź do UICollectionViewCellpodklasy i wklej następujący kod, tak jak jest w klasie.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

Nawiasem mówiąc, używam Xcode 7.3.1 i Swift 2.3. Rozwiązanie jest testowane na systemie iOS 9.3, który działa bez zarzutu.

Dzięki, mam nadzieję, że to pomogło.

po zakończeniu
źródło
przepraszam za mój zły angielski, miałem na myśli "działa jak urok" :)
Aznix
@Aznix No issue .. :)
onCompletion
2

W swift umieść następujący kod w podklasie komórki widoku kolekcji:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
źródło
To była odpowiedź, której szukałem. Kiedyś nadpisałem setBounds w Objective-C, aby naprawić ten problem (nie byłem pewien, jak napisać to w Swift). Dziękuję :)
Matthew Cawley
1

Zauważyłem, że contentVieww iOS 8 występują również problemy z dopasowywaniem rozmiaru. Zwykle jest on układany bardzo późno w cyklu, co może powodować tymczasowe konflikty z ograniczeniami. Aby rozwiązać ten problem, dodałem następującą metodę w kategorii UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Ta metoda powinna zostać wywołana po odkolejkowaniu komórki.

phatmann
źródło
Byłoby idealnie, gdyby ta metoda była wywoływana automatycznie. Nie podoba mi się to, ale można to zrobić przez swizzling dequeueReusableCellWithReuseIdentifier:forIndexPath:.
phatmann
Wygląda na to, że firma Apple naprawiła ten problem w wersji 8.1 zestawu iOS SDK w Xcode 6.1 GM (kompilacja 6A1042b). Dlatego zaktualizowałem powyższy kod, aby nie działał podczas korzystania z zestawu SDK 8.1. Po przeniesieniu całego zespołu do Xcode 6.1 możesz całkowicie usunąć ten hack.
phatmann
1

Naprawiłem to:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

zobacz: tutaj

Serluca
źródło
-1

Po prostu upewnij się, że zaznaczyłeś pole wyboru „Autoresize subviews” w końcówce tej komórki widoku kolekcji. Będzie działać dobrze zarówno na iOS 8, jak i iOS 7.

Deepak GM
źródło
Nie, nie ma. To jest wbudowana komórka prototypowa.
dzięk.