iCulture forum | iPhone, iPad,  iPod touch, Apple TV en iOS

iCulture forum | iPhone, iPad, iPod touch, Apple TV en iOS (https://forum.iculture.nl/)
-   Ontwikkelen voor iOS (https://forum.iculture.nl/f133/development/f58/ontwikkelen-voor-ios/)
-   -   UITableView refresh (https://forum.iculture.nl/f133/development/f58/ontwikkelen-voor-ios/94945-uitableview-refresh.html)

DJ14 08-07-11 11:39

UITableView refresh
 
Ik ben bezig met een app met een UITableView die gerefresht wordt. Het refreshen doe ik met de pull-to-refresh techniek. Aan het eind van de refresh heb ik een array gevuld met 'nieuwe' objecten. De vraag is: hoe krijg ik deze nieuwe objecten 'bovenop' de bestaande rijen in de tableview, terwijl de scrollview binnen de tableview qua positie hetzelfde blijft? Dit is terug te vinden in veel apps, zoals De Telegraaf (helemaal naar onder scrollen om meer artikelen te laden, artikelen worden aan de tableview gehangen, terwijl het scherm hetzelfde blijft) en Tweetie (of Twitter for iPhone). Echter ben ik er nog steeds niet achter hoe dit gedaan wordt. Weet iemand dit? Ik heb al geprobeerd scrollToRowAtIndexPath te gebruiken, maar dit geeft niet het gewenste effect. Bij voorbaat dank.

Whacko 08-07-11 13:17

Ik neem aan dat je "[tableView reloadData]" aanroept om te verversen? dan ververst ie de hele tableview. Je moet de rows gewoon inserten:

Code:

[theTableView beginUpdates];
[theTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:animation];
// make sure the dataSource will return new rows before calling endUpdates
[theTableView endUpdates];

Je krijgt dan als het goed is gewoon een aantal "cellForRowAtIndexPath:" calls voor de nieuwe rows.

wubbe 08-07-11 13:34

Je wilt aan de bovenkant nieuwe rijen toevoegen aan de tabel, terwijl de scrollpositie hetzelfde blijft?

Dan zou ik in ieder geval niet [tableView reload] gebruiken (als je dat al deed :o) maar:
Code:

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Dat levert wel wat geklooi op, want als je een rij toegevoegd hebt dan moet meteen ook de returnwaarde van numberOfRowsInSection kloppen. En dat kun je weer oplossen door alles wat met de insert te maken heeft tussen beginUpdates en endUpdates te zetten.

Probeer eens ook de scrollToRowAtIndexPath binnen de begin- en endUpdates te zetten. Wellicht dat het scrollen dan wel goed gaat.
Alle API Info vind je natuurlijk hier.

Heb ik je een beetje geholpen? Ik ga binnenkort aan een soortgelijke toepassing werken, dus ik ben benieuwd naar het resultaat ;)

(ik gebruik nu EGORefresh voor de pull-to-refresh en dat werkt feilloos)

DJ14 08-07-11 15:31

Kijk, daar heb ik wat aan :D. Thanks voor jullie replies. Ik was al eerder aan het knoeien met insertRowsAtIndexPaths, maar heb het nog niet werkend gekregen. Eerst telkens een error met "NSMutableArray index .. beyond bounds". Die is opgelost door de datasource te wijzigingen tussen [self.tableView beginUpdates] en [self.tableView endUpdates]. Nu heb ik volgende code:

Code:

NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
        for(int i; i<[arrayWithNewObjects count];i++){
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
            [indexPaths addObject:indexPath];
        }

        [self.tableView beginUpdates];
        [dataSourceArray insertObjects:arrayWithNewObjects atIndexes:[NSIndexSet indexSetWithIndexesInRange:range]];
        [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];

Echter crasht de app nu en krijg ik in de console de melding:
Code:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (170) must be equal to the number of rows contained in that section before the update (167), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted).
Waarom krijg ik deze error terwijl ik in mijn code wel gewoon nieuwe rows insert (0 inserted is dus niet waar)?

ps: @wubbe: jup ik gebruik ook EGORefresh, werkt idd zonder problemen

wubbe 08-07-11 15:45

Da's nou precies de melding waar ik je voor wilde waarschuwen. :)

0 inserted is inderdaad niet waar, maar tussendoor roept hij numberOfRowsInSection aan (zet er maar eens een debug-stopje in) en die geeft dan nog 0 terug. Vandaar dat ie denkt dat het niet klopt. Volgens mij moet je er voor zorgen dat voordat je endupdates aanroept alles goed staat zodat de methoden numberOfSections en numberOfRowsInSection de correcte getallen terug geven.

Tip: zet NSLog()'s in numberOfRowsInSection en NumberOfSections. Dan zie je wanneer ze aangeroepen worden en wat ze terug geven. Daar zit nog iets fout.

Ik heb daar zelf enorm mee lopen stoeien. Wel een hoop wijzer geworden maar ik heb het nog niet op een elegante manier opgelost. Geluk voor mij is het project even stopgezet. Na mijn vakantie, (in september dus) ga ik er weer mee verder. En dan staat hier natuurlijk dé oplossing ;)

DJ14 08-07-11 16:10

Helaas, deze app moet voor het eind van de zomervakantie af zijn :P. Dat wordt nog even zelf knoeien vrees ik. Het valt me trouwens op dat over dit onderwerp geen concreet voorbeeld op internet te vinden is, terwijl deze techniek in zoveel apps wordt gebruikt... (op internet zie ik dingen over contentoffset, scrollen, het inserten van rows; kortom: vanalles doorelkaar zonder dat iemand de juiste,werkende oplossing aangeeft).

wubbe 08-07-11 16:20

Ja, dat viel mij ook op. als je bijvoorbeeld naar 'Trein' kijkt, dan zie je dat Dennis Stevense dit principe echt heel goed voor elkaar had.

Er moet een vaste methode zijn waarmee je inserts en deletes van een tabel mooi met animaties kunt doen. Maar daar gaan we uitkomen :)

En... je weet precies wat het probleem is. Dan is een stap naar de oplossing niet ver.

DJ14 08-07-11 18:23

En ik ben weer een stapje verder :D. De crash is verdwenen en de rows worden geanimeerd in de tableview gegooid:

Code:

        NSRange range = NSMakeRange(0,[arrayWithNewObjects count]);
        NSRange range2 = NSMakeRange((dataSourceArray-[arrayWithNewObjects count]),[arrayWithNewObjects count]);
        NSLog(@"RANGE: %@",NSStringFromRange(range));
        NSLog(@"RANGE2: %@",NSStringFromRange(range2));
        NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"Date" ascending:NO];
        NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
        NSArray *sortedNewObjects;
        sortedNewObjects = [arrayWithNewObjects sortedArrayUsingDescriptors:sortDescriptors];
       

        NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
        for(int i; i<[arrayWithNewObjects count];i++){
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
            [indexPaths addObject:indexPath];
        }

        [self.tableView beginUpdates];
        [dataSourceArray removeObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range2]];
        [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
        [dataSourceArray insertObjects:sortedNewObjects atIndexes:[NSIndexSet indexSetWithIndexesInRange:range]];
        [self.tableView endUpdates];

Echter wat nu gebeurt is nog niet het gewenste effect dat we terugvinden bij Tweetie (Twitter for Iphone) en de Telegraaf app... Want wat er nu gebeurt is het volgende: tableview naar boven slepen --> er wordt herladen --> tableview blijft niet staan op oude data, maar nieuwe data komt gelijk in scherm (wat er gebeurt is dat de top van de tableview in scherm blijft (dus zeg maar row 0), maar wat er moet gebeuren is dat het scherm hetzelfde blijft, en de top van de tableview veel hoger komt (nieuwe rows komen bovenop ouden, terwijl scherm/scrollview op laatste oude blijft)). Het is een beetje moeilijk uitleggen, maar als jullie Twitter for iPhone kennen/gebruiken hoop ik dat jullie weten wat ik bedoel. Waar ik nu aan zit te denken is iets met de offset van de top van de tableview: huidige offset in NSUserDefaults gooien --> hoogte van aantal nieuwe rows berekenen --> nieuwe offset = oude offset + hoogte van nieuwe rows. Het probleem hierbij is echter dat de tableview rows verschillende hoogten hebben... Wat denken jullie wat nu het beste is?

DJ14 09-07-11 09:48

GEWELDIG!!!111!! HET WERKT :D:D. Voor mensen die deze techniek ook willen gebruiken: het heeft niets te maken met insertRowsAtIndexPaths en deleteRowsAtIndexPaths maar met de contentOffset van de tableview. Globaal ziet het er zo uit:

Code:

-> actie: user heeft tableview naar beneden getrokken -> gevolg: refresh
- array met nieuwe data vullen
- [tableView reloadData] aanroepen
- huidige contentOffset van tableview pakken
- hoogte van nieuwe rijen berekenen (bij dynamische cel hoogte gebruik je [NSString sizeWithFont:constrainedToSize:lineBreakMode]
- hoogte van nieuwe rijen optellen bij hoogte van huidige offset
- dit getal toekennen aan self.tableView.contentOffset.y (self.tableView.contentOffset.x = 0)

Wellicht dat ik nog een blogpost aan dit onderwerp zal wijden, in ieder geval als iemand hier hulp mee nodig heeft weet die mij te vinden.

Whacko 11-07-11 13:16

Mooi dat je hebt wat je wilt hebben. Je kunt het inderdaad met de contentOffset doen, maar met insertRowsAtIndexpathss is eigenlijk de juiste manier. Maar dat is inderdaad soms lastig voor elkaar te krijgen.

edit:
Misschien heb je hier nog wat aan:

Code:

[self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionBottom animated:false];

wubbe 18-07-11 15:39

Je gebruikt dus niet het 'geanimeerd' toevoegen van nieuwe rijen in je tabel?

DJ14 19-07-11 15:52

Citaat:

Oorspronkelijk geplaatst door wubbe (Bericht 691997)
Je gebruikt dus niet het 'geanimeerd' toevoegen van nieuwe rijen in je tabel?

Nee, dat is me niet gelukt.

wubbe 19-07-11 15:55

Nou, dan moet ik het dus zelf uitzoeken :mad:
Fraai is dat ;)

Als ik het uitgepuzzeld heb dan meld ik dat wel hier.

DJ14 19-07-11 15:59

Citaat:

Oorspronkelijk geplaatst door wubbe (Bericht 692310)
Nou, dan moet ik het dus zelf uitzoeken :mad:
Fraai is dat ;)

Als ik het uitgepuzzeld heb dan meld ik dat wel hier.

Ik ben benieuwd naar het resultaat, Whacko zei ook al dat dat een betere manier is :)

Citaat:

Je kunt het inderdaad met de contentOffset doen, maar met insertRowsAtIndexpathss is eigenlijk de juiste manier. Maar dat is inderdaad soms lastig voor elkaar te krijgen.


Alle tijden zijn GMT +2. Het is nu 08:41.