iOS 8 automatische celhoogte – kan niet naar de laatste rij scrollen

Ik gebruik iOS 8 nieuwe self-sizing cellen. Visueel werkt het goed – elke cel krijgt de juiste maat. Als ik echter probeer naar de laatste rij te scrollen, lijkt de tabelweergave niet de juiste grootte te kennen. Is dit een bug of is daar een oplossing voor?

Zo kunt u het probleem opnieuw maken:

Dit project gebruiken – TableViewCellWithAutoLayoutiOS8(verwezen vanuit dit ZO-antwoord), kreeg ik de cellen voor automatisch wijzigen van de grootte zoals verwacht.

Als ik echter de functie scrollToRowAtIndexPathaanroep, als volgt:

tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)

Ik kom niet bij de laatste rij– Ik kom er pas halverwege.

Zelfs door te proberen een functie op een lager niveau te gebruiken, zoals deze:

tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true)

Het resultaat is niet zoals verwacht, het zal niet tot het einde komen. Als ik er vaak op klik of even wacht, komt het uiteindelijk op de juiste plek. Het lijkt erop dat de tableView.contentSize.height niet correct is ingesteld, dus de iOS “weet niet” waar die laatste cel is.

Zou alle hulp op prijs stellen.

Bedankt


Antwoord 1, autoriteit 100%

Update: 24 juni 2015

Apple heeft de meeste van deze bugs verholpen vanaf de iOS 9.0 SDK. Alle problemen zijn opgelost vanaf iOS 9 beta 2, inclusief scrollen naar boven & onderaan de tabelweergave zonder animatie, en reloadDataaanroepen terwijl u in het midden van de tabelweergave scrolt.

Dit zijn de resterende problemen die nog niet zijn opgelost:

  1. Als u een grote geschatte rijhoogte gebruikt, zorgt het scrollen naar de laatste rij met animatie ervoor dat de tabelweergavecellen verdwijnen.
  2. Als u een kleine geschatte rijhoogte gebruikt, zorgt het scrollen naar de laatste rij met animatie ervoor dat de tabelweergave te vroeg klaar is met scrollen, waardoor enkele cellen onder het zichtbare gebied achterblijven (en de laatste rij nog steeds buiten het scherm).

Er is een nieuw bugrapport (rdar://21539211) ingediend voor deze problemen met scrollen met animatie.

Oorspronkelijk antwoord

Dit is een Apple-bug met de schatting van de rijhoogte in de tabelweergave, en het bestaat al sinds deze functionaliteit voor het eerst werd geïntroduceerd in iOS 7. Ik heb rechtstreeks met Apple UIKit-ingenieurs en ontwikkelaarsevangelisten aan dit probleem gewerkt — zij hebben erkend dat het is een bug, maar heb geen betrouwbare oplossing (behalve het uitschakelen van de schatting van de rijhoogte) en leek niet echt geïnteresseerd in het oplossen ervan.

Merk op dat de bug zich op andere manieren manifesteert, zoals het verdwijnen van tabelweergavecellen wanneer u reloadDataaanroept terwijl u gedeeltelijk of volledig naar beneden scrolt (bijv. contentOffset.yis aanzienlijk groter dan 0).

Het is duidelijk dat met iOS 8-cellen met zelfafmetingen het schatten van de rijhoogte van cruciaal belang is, dus Apple moet dit zo snel mogelijk aanpakken.

Ik heb dit probleem op 21 oktober 2013 ingediend als Radar #15283329. Dien dubbele bugrapporten in, zodat Apple prioriteit geeft aan een oplossing.

Je kunt dit eenvoudige voorbeeldprojectbijvoegen om het probleem te demonstreren. Het is rechtstreeks gebaseerd op de eigen voorbeeldcode van Apple.


Antwoord 2, autoriteit 61%

Dit was een erg vervelende bug, maar ik denk dat ik een permanente oplossing heb gevonden, hoewel ik niet helemaal kan uitleggen waarom.

Bel de functie aan na een kleine (onopgemerkte) vertraging:

let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
  tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)
})

Vertel me of dit ook voor jou werkt.


Antwoord 3, autoriteit 39%

Het is zeker een bug van Apple. Ik heb ook dit probleem. Ik heb dit probleem opgelost door de methode “scrollToRowAtIndexPath” tweemaal aan te roepen. Voorbeeldcode is:

       if array.count > 0 {
        let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0)
        self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        let delay = 0.1 * Double(NSEC_PER_SEC)
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
        dispatch_after(time, dispatch_get_main_queue(), {
            self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        })
    }

Antwoord 4, autoriteit 15%

Ik heb een tijdelijke oplossing gevonden die van pas kan komen totdat Apple besluit de vele bugs te verhelpen die ons teisteren.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *text = [self findTextForIndexPath:indexPath];
    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13];
    CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT)
                                                options:NSStringDrawingUsesLineFragmentOrigin
                                             attributes:@{NSFontAttributeName: font}
                                                context:nil];
    return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING;
}

Dit is niet perfect, maar het deed het werk voor mij. Nu kan ik bellen:

- (void)scrollToLastestSeenMessageAnimated:(BOOL)animated
{
    NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0];
    if (count > 0) {
        NSInteger lastPos = MAX(0, count-1);
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

Op viewDidLayoutSubviewsen het vindt de juiste plaats onderaan (of een zeer dichte geschatte positie).

Ik hoop dat dat helpt.


Antwoord 5, autoriteit 3%

Voor mijn geval vond ik een tijdelijke oplossing door geen geschatte celhoogte aan het programma voor te stellen. Ik deed dit door commentaar te geven op de volgende methode in mijn code:

- (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

Houd er echter rekening mee dat dit de gebruikerservaring kan beïnvloeden wanneer de gebruiker scrolt, als uw cellen veel van elkaar verschillen. Voor mijn geval tot nu toe geen merkbaar verschil.

Hopelijk helpt het!


Antwoord 6, autoriteit 3%

Ik heb dit probleem nog in Swift 5 iOS 13, dit heeft mijn probleem opgelost

DispatchQueue.main.async { [weak self] in
    self?.tableView.reloadData()
    self?.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
 }

Antwoord 7

Mijn oplossing was om de grootte van het storyboard als schatting te gebruiken.

Dus in plaats van dit:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {   
return UITableViewAutomaticDimension;

}

Ik heb zoiets als dit gedaan:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 
MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath];
switch (messageType) {
    case MyMessageTypeText:
        return 45;
        break;
    case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText:
        return 96;
        break;
    default:
        break;
 }
}

Ik ben een chattabel aan het schrijven, dus het is waarschijnlijk dat veel van mijn cellen, met name dat teksttype, groter zullen zijn dan wat er in IB staat, vooral als het chatbericht erg lang is. Dit lijkt een redelijk goede … nou ja … schatting en naar beneden scrollen komt redelijk dichtbij. Het lijkt iets erger te worden naarmate het scrollen langer wordt, maar dat is te verwachten denk ik


Antwoord 8

Bel tableview reloadData nadat viewDidAppear het probleem kan oplossen

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}

Antwoord 9

Hoewel als smileyborg’s antwoordhet een bug is in iOS 8.x, zou het opgelost moeten zijn op alle platforms die je ondersteunt …

Als tijdelijke oplossing voor pre-iOS9, kun je onderstaande code gebruiken zonder dispatch_async of dispatch_after.
Getest op iOS 8.4-simulator.

UPDATE: het aanroepen van (alleen) layoutIfNeeded werkt niet wanneer de weergavecontroller zichtbaar wordt door UIPageViewController te scrollen. Gebruik dus layoutSubviews (of misschien setNeedsLayout + layoutIfNeeded) in plaats daarvan.

// For iOS 8 bug workaround.
// See https://stackoverflow.com/a/33515872/1474113
- (void)scrollToBottomForPreiOS9
{
    CGFloat originalY, scrolledY;
    do {
        // Lay out visible cells immediately for current contentOffset.
        // NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged.
        [self.tableView layoutSubviews];
        originalY = self.tableView.contentOffset.y;
        [self scrollToBottom];  // Call -scrollToRowAtIndexPath as usual.
        scrolledY = self.tableView.contentOffset.y;
    } while (scrolledY > originalY);
}

Antwoord 10

Ik had hetzelfde probleem bij het maken van een chattabelweergave met verschillende celhoogtes. Ik roep de onderstaande code in viewDidAppear() levenscyclusmethode:

// First figure out how many sections there are
let lastSectionIndex = self.tableView.numberOfSections - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)
// Make the last row visible
self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)

Laat me weten of dat ook voor jou heeft gewerkt.


Antwoord 11

Klik in het storyboard-venster in een leeg gebied om alle weergaven te deselecteren, klik vervolgens op de weergave met de tabelweergave erin en klik vervolgens op het pictogram Resolve Auto Layout Issueen selecteer Reset to Suggested Constraints

voer hier de afbeeldingsbeschrijving in
voer hier de afbeeldingsbeschrijving in


Antwoord 12

Gebruik deze eenvoudige code om naar beneden te scrollen

var rows:NSInteger=self.tableName.numberOfRowsInSection(0)
        if(rows > 0)
        {
            let indexPath = NSIndexPath(forRow: rows-1, inSection: 0)
            tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition:  UITableViewScrollPosition.Bottom, animated: true)
        }
            }

Other episodes