I'm facing a issue where when I fetch SKProduct locale price on to the label.text of my UICollectionView, Its displaying correctly, however the prices jump around in different labels depending on how fast the page is being loaded.
TopUpViewController (UICollectionView) -> TopUpMoneyCell (MoneyLabel)
Any idea how to solve this? Currently I'm calling SKProduct in TopUpMoneyCell. I have a array of 4 in app product to be called, each with different value.
Should I call it from TopUpViewController instead? If so, how should I pull the data I got from TopUpViewController to TopUpMoneyCell?
Edit 1: Added UI Mockup, once again thanks for commenting.
View of UI Mockup
Take a look at overriding the prepareForReuse() function in your TopUpMoneyCell. In that function, cancel any asynchronous calls that you may have started to load the price.
Without seeing any code, it's a little difficult to see what you're doing, but it sounds like you might want to have your TopUpViewController conform to the UICollectionViewDataSource protocol (https://developer.apple.com/documentation/uikit/uicollectionviewdatasource?language=objc)
Then you could implement something like the following there:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell* cell = [self.testCollectionView dequeueReusableCellWithReuseIdentifier:#"SomeReuseID"
forIndexPath:indexPath];
// set your custom labeling on the cell here (you probably have a subclass)
return cell;
}
Don't forget to tell the UICollectionView what to use for its dataSource.
From what you are describing, it sounds like your cells are querying some source of data directly, so that when the UICollectionView chooses to reuse a cell, the labels are not in the order you intend.
Okay I managed to solve my issue. Would appreciate if anyone could help to simplify my code :) Below is an extract of the code.
#import "TopUpMoneyCell.h"
#import "TopUpMoneyModel.h"
#import <StoreKit/StoreKit.h>
#interface TopUpMoneyCell() <SKProductsRequestDelegate>
#property (nonatomic,strong) NSString *purchID;
#property (nonatomic,strong) NSArray *products;
#property (nonatomic,strong) NSArray *productID;
#property (nonnull,strong) IAPCompletionHandle handle;
#property (strong,nonatomic) SKProductsRequest *request;
#property (strong,nonatomic) SKProduct *firstProduct;
#property (strong,nonatomic) SKProduct *secondProduct;
#property (strong,nonatomic) SKProduct *thirdProduct;
#property (strong,nonatomic) SKProduct *fourthProduct;
#property (strong,nonatomic) NSString *firstProductPrice;
#property (strong,nonatomic) NSString *secondProductPrice;
#property (strong,nonatomic) NSString *thirdProductPrice;
#property (strong,nonatomic) NSString *fourthProductPrice;
#property (nonatomic, weak) UILabel *firstPriceLabel;
#property (nonatomic, weak) UILabel *secondPriceLabel;
#property (nonatomic, weak) UILabel *thirdPriceLabel;
#property (nonatomic, weak) UILabel *fourthPriceLabel;
#end
#implementation AQTopUpMoneyCell
- (UILabel *)priceLabel {
if (!_priceLabel) {
UILabel *label = [[UILabel alloc] init];
_priceLabel = label;
label.textAlignment = NSTextAlignmentCenter;
label.text = #"";
}
return _priceLabel;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self productsInfoRequest];
[self cnyLabel];
}
return self;
}
- (void)productsInfoRequest {
_productID = [NSArray arrayWithObjects:#"100",#"200",#"300",#"400", nil];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:_productID]];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSSortDescriptor *mySortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"price" ascending:NO];
NSMutableArray *_productsAvailable = [[NSMutableArray alloc] initWithArray:response.products];
[_productsAvailable sortUsingDescriptors:[NSArray arrayWithObject:mySortDescriptor]];
self.firstProduct = response.products[0];
self.secondProduct = response.products[1];
self.thirdProduct = response.products[2];
self.fourthProduct = response.products[3];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:_firstProduct.priceLocale];
self.firstProductPrice = [numberFormatter stringFromNumber:_firstProduct.price];
self.secondProductPrice = [numberFormatter stringFromNumber:_secondProduct.price];
self.thirdProductPrice = [numberFormatter stringFromNumber:_thirdProduct.price];
self.fourthProductPrice = [numberFormatter stringFromNumber:_fourthProduct.price];
static int counter = 0;
if (counter == 4) {
counter = 0;
}
if ( [_productsAvailable count] > 0){
if (_indexPath.row == 0)
{[self.priceLabel setText:_firstProductPrice];}
if (_indexPath.row == 1)
{[self.priceLabel setText:_secondProductPrice];}
if (_indexPath.row == 2)
{[self.priceLabel setText:_thirdProductPrice];}
if (_indexPath.row == 3)
{[self.priceLabel setText:_fourthProductPrice];}
counter++;
}
}
#pragma mark - SKProductsRequestDelegate
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
#if DEBUG
NSLog(#"log-IAP> request -> failedWithError: %#", error);
[SVProgressHUD showErrorWithStatus:#"Request timed out. Try again later."];
#endif
}
- (void)requestDidFinish:(SKRequest *)request{
#if DEBUG
NSLog(#"log-IAP> requestDidFinish?");
#endif
}
- (void)setIndexPath:(NSIndexPath *)indexPath {
_indexPath = indexPath;
NSString *imageName = [NSString stringWithFormat:#"touupbg_%ld",indexPath.row];
self.bgImageV.image = [UIImage imageNamed:imageName];
if (indexPath.row == 0) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(367*scale));
make.width.equalTo(kScale(308*scale));
}];
} else if (indexPath.row == 1) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(380*scale));
make.width.equalTo(kScale(308*scale));
}];
}else if (indexPath.row == 2) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(373*scale));
make.width.equalTo(kScale(316*scale));
}];
}else if (indexPath.row == 3) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(403*scale));
make.width.equalTo(kScale(308*scale));
}];
}else if (indexPath.row == 4) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(400*scale));
make.width.equalTo(kScale(308*scale));
}];
}else if (indexPath.row == 5) {
[self.bgImageV mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(kScale(401*scale));
make.width.equalTo(kScale(317*scale));
}];
}
}
#end
Related
I'm getting correct data after loading the page as shown in this screen
getting correct data even after performing search like this image
but after search is cancelled im getting wrong data (label should be ph:+ and Fax:+ but its displaying both as Fax:+ like this
with this code
#implementation Staffdir
#synthesize tableview,filteredContentList,searchBar1;
- (void)viewDidLoad {
searchBar1.delegate = self;
__block StaffDirectoryModel*staffdirectorymodel;
NSURL *url = [NSURL URLWithString:#"http://dev.devobal.com/GetData.aspx?Query=select%20*%20from%20tb_RHP_Staff_Directory"];
_staffdirarr=[[NSMutableArray alloc]init];
NSMutableURLRequest *req=[NSMutableURLRequest requestWithURL:url];
[[[NSURLSession sharedSession]dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSMutableArray *jsonarr=[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI
for(int i=0;i<jsonarr.count;i++){
staffdirectorymodel=[[StaffDirectoryModel alloc]init];
staffdirectorymodel.name =[[jsonarr objectAtIndex:i]objectForKey:#"Name"];
staffdirectorymodel.occupation=[[jsonarr objectAtIndex:i]objectForKey:#"Job_Title"];
staffdirectorymodel.phonelabel =[[jsonarr objectAtIndex:i]objectForKey:#"Phone"];
staffdirectorymodel.faxlabel =[[jsonarr objectAtIndex:i]objectForKey:#"Fax_Number"];
staffdirectorymodel.department =[[jsonarr objectAtIndex:i]objectForKey:#"Department"];
[_staffdirarr addObject:staffdirectorymodel];
staffdirectorymodel=nil;
}
[tableview reloadData];
});
}]resume];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
//-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// return 1;
//}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (isSearching) {
return [searchResults count];
}else{
return _staffdirarr.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellidentifer = #"cell";
StaffDirectoryModel*staffdirectorymodel=[[StaffDirectoryModel alloc]init];
Staffcustom *cell= [tableView dequeueReusableCellWithIdentifier:cellidentifer];
if (cell == nil) {
cell = [[Staffcustom alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifer];
}
[cell.fxlabel setHidden:NO];
[cell.faxnumlabel setHidden:NO];
if (isSearching) {
staffdirectorymodel=[searchResults objectAtIndex:indexPath.row];
cell.namelabel.text = staffdirectorymodel.name;
cell.occupationlabel.text = staffdirectorymodel.occupation;
NSString*tempph=staffdirectorymodel.phonelabel;
NSString*tempfax=staffdirectorymodel.faxlabel;
if(tempph.length!=0)
{
cell.phonelabel.text = staffdirectorymodel.phonelabel;
}
else{
cell.phlabel.text=#"Fax: +";
cell.phonelabel.text = staffdirectorymodel.faxlabel;
[cell.fxlabel setHidden:YES];
[cell.faxnumlabel setHidden:YES];
}
if(tempfax.length==0)
{
[cell.faxnumlabel setHidden:YES];
[cell.fxlabel setHidden:YES];
}
if([tempph length]!=0 &&[tempfax length]!=0){
cell.faxnumlabel.text = staffdirectorymodel.faxlabel;
}
cell.Depart.text = staffdirectorymodel.department;
}
else{
staffdirectorymodel=[_staffdirarr objectAtIndex:indexPath.row];
cell.namelabel.text = staffdirectorymodel.name;
cell.occupationlabel.text = staffdirectorymodel.occupation;
NSString*tempph=staffdirectorymodel.phonelabel;
NSString*tempfax=staffdirectorymodel.faxlabel;
if(tempph.length!=0)
{
cell.phonelabel.text = staffdirectorymodel.phonelabel;
}
else{
cell.phlabel.text=#"Fax: +";
cell.phonelabel.text = staffdirectorymodel.faxlabel;
[cell.fxlabel setHidden:YES];
[cell.faxnumlabel setHidden:YES];
}
if(tempfax.length==0)
{
[cell.faxnumlabel setHidden:YES];
[cell.fxlabel setHidden:YES];
}
if([tempph length]!=0 &&[tempfax length]!=0){
cell.faxnumlabel.text = staffdirectorymodel.faxlabel;
}
cell.Depart.text = staffdirectorymodel.department;
}
cell.namelabel.textColor = [UIColor blueColor];
cell.occupationlabel.textColor=[UIColor colorWithRed:0.5 green:0.0 blue:1 alpha:1.0];
UIView * additionalSeparator = [[UIView alloc] initWithFrame:CGRectMake(0,cell.frame.size.height-1,cell.frame.size.width,0.1)];
additionalSeparator.backgroundColor = [UIColor blueColor];
[cell addSubview:additionalSeparator];
return cell;
}
//- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// isSearching = YES;
//}
-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)searchText
{
//[filteredContentList removeAllObjects];
if(searchText.length!=0){
isSearching=YES;
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"name contains[c] %#",
searchText];
searchResults=(NSMutableArray *)[_staffdirarr filteredArrayUsingPredicate:resultPredicate];
[tableview reloadData];
}
else{
isSearching=NO;
[tableview reloadData];
}
//
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
isSearching=YES;
[tableview reloadData];}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
isSearching=NO;
[tableview reloadData];}
and
staffdirectory model class
#interface StaffDirectoryModel : NSObject
#property (nonatomic,retain) NSString*name;
#property (nonatomic,retain) NSString*occupation;
#property (nonatomic,retain) NSString*phlabel;
#property (nonatomic,retain) NSString*phonelabel;
#property (nonatomic,retain) NSString*fxlabel;
#property (nonatomic,retain) NSString*faxlabel;
#property (nonatomic,retain) NSString*department;
staffcustom.h
#interface Staffcustom : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *fxlabel;
#property (weak, nonatomic) IBOutlet UILabel *namelabel;
#property (weak, nonatomic) IBOutlet UILabel *occupationlabel;
#property (weak, nonatomic) IBOutlet UILabel *phlabel;
#property (weak, nonatomic) IBOutlet UILabel *faxlabel;
#property (weak, nonatomic) IBOutlet UILabel *phonelabel;
#property (weak, nonatomic) IBOutlet UILabel *faxnumlabel;
#property (nonatomic,retain) IBOutlet UILabel *Depart;
#end
I have project with normal cell and working success but I want to change it with custom cell I added Cell.h and Cell.m files into my project and I need to integrate custom cell to my working cell. And last I want to show detail view title description and image (I added title codes working) My codes under
Cell.h
#import <UIKit/UIKit.h>
#interface Cell : UITableViewCell
#property (nonatomic, weak) IBOutlet UIImageView *imaj;
#property (nonatomic, weak) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
#end
Cell.m
#import "Cell.h"
#implementation Cell
#synthesize imaj = _imaj;
#synthesize descriptionLabel = _descriptionLabel;
#synthesize titleLabel = _titleLabel;
#end
ViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
#import "MBProgressHUD.h"
#import "Cell.h"
static NSString *const kConsumerKey = #"a1SNULSPtp4eLQTsTXKKSgXkYB5H4CMFXmleFvqE";
#interface MasterViewController () <UISearchBarDelegate, UISearchDisplayDelegate,MBProgressHUDDelegate>{
MBProgressHUD *HUD;
}
#property (nonatomic, assign) NSInteger currentPage;
#property (nonatomic, assign) NSInteger totalPages;
#property (nonatomic, assign) NSInteger totalItems;
#property (nonatomic, assign) NSInteger maxPages;
#property (nonatomic, strong) NSMutableArray *activePhotos;
#property (strong, nonatomic) NSMutableArray *staticDataSource;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.activePhotos = [[NSMutableArray alloc] init];
self.searchResults = [[NSMutableArray alloc] init];
self.staticDataSource = [[NSMutableArray alloc] init];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self loadPhotos:self.currentPage];
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// if (self.currentPage == self.maxPages
// || self.currentPage == self.totalPages
// || self.currentPage == self.totalPages
// || self.totalItems == self.photos.count) {
// return self.photos.count;
// } else if (self.tableView == self.searchDisplayController.searchResultsTableView){
// return [self.searchResults count];
//
// }
// return self.photos.count + 1;
return self.activePhotos.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.currentPage != self.maxPages && indexPath.row == [self.staticDataSource count] - 1 ) {
[self loadPhotos:++self.currentPage];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.row == [self.activePhotos count]) {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Loading";
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
} else {
NSDictionary *photoItem = self.activePhotos[indexPath.row];
cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// cell.textLabel.text = [self.searchResults[indexPath.row] valueForKey:#"name"];
// } else {
// NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"] ] placeholderImage:[UIImage imageNamed:#"placeholder.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
// NSLog(#"%#",self.searchResults);
return cell;
}
- (void)myTask {
// Do something usefull in here instead of sleeping ...
sleep(1.5);
}
#pragma mark UISearchDisplay delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// [self.searchResults removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
self.activePhotos = [NSMutableArray arrayWithArray:[self.staticDataSource filteredArrayUsingPredicate:resultPredicate]];
//[self.tableData filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)loadPhotos:(NSInteger)page
{
NSString *apiURL = [NSString stringWithFormat:#"https://api.500px.com/v1/photos?feature=editors&page=%ld&consumer_key=%#",(long)page,kConsumerKey];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:apiURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSMutableDictionary *jsonObject = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog(#"%#",jsonObject);
[self.staticDataSource addObjectsFromArray:[jsonObject objectForKey:#"photos"]];
self.currentPage = [[jsonObject objectForKey:#"current_page"] integerValue];
self.totalPages = [[jsonObject objectForKey:#"total_pages"] integerValue];
self.totalItems = [[jsonObject objectForKey:#"total_items"] integerValue];
self.activePhotos = self.staticDataSource;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}] resume];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.StoreList = [self.activePhotos objectAtIndex:indexPath.row];
}
#end
Also I uploaded working project here
http://www.filedropper.com/needcustomcell
In your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell;
// rest of your code
}
that would do it. You have used UITableViewCell you need to replace ot with your custom cell class.
I have a custom UITableViewCell created in a .xib and add it to a TableView. The cell contains a Button to download some data. On Button click the download starts and the Button disappears to show a cancel Button and a custom View with a download progress. After the download is finished I update my model and reload the rows in the visible area of the app.
When I debug, I see that the cellForRowAtIndexPath-methode get called and the model got updated. This means the cancel-Button and the progress-View get set hidden = YES; But they don't disappear. After I scroll the cell out of view and back in, the progress-View is hidden but the cancel-Button not.
The TableView Methodes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifierHeaderCell = #"PodcastListHeaderCell";
static NSString *cellIdentifierBodyCell = #"PodcastListBodyCell";
// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EEE, d MMM yyyy HH:mm:ss Z"];
if(indexPath.row == 0) {
MGPodcastListHeaderCell *cell = (MGPodcastListHeaderCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierHeaderCell];
if (cell == nil)
{
...
}
return cell;
}
else {
MGPodcastListBodyCell *cell = (MGPodcastListBodyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierBodyCell];
if (cell == nil) {
UIViewController *controller = [[UIViewController alloc] initWithNibName:#"MGPodcastListBodyCell" bundle:nil];
cell = (MGPodcastListBodyCell *)controller.view;
NSMutableDictionary *mediaIntem = self.mediaData[(NSUInteger) (indexPath.row-1)];
cell.mediaTitleLabel.text = mediaIntem[#"title"];
NSDate *date = [dateFormat dateFromString:mediaIntem[#"pubDate"]];
cell.pubDateLabel.text = [date descriptionWithLocale:[NSLocale currentLocale]];
cell.durationLabel.text = mediaIntem [#"duration"];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
cell.podcastId = (NSInteger) (indexPath.row-1);
cell.cellPlayState = [[MGPlayState alloc] initWithPlayState:(NSInteger) [mediaIntem[#"playState"] integerValue]];
[cell setPodcastCellDelegate:self];
}
return cell;
}
}
-(void) downloadButtonPressedOfCell:(NSInteger)podcastId {
APConnection *con = [[APConnection alloc] init];
BOOL reachable = [con reachableHost];
if (reachable)
{
//============Get Media Item =============================
NSMutableDictionary *mediaDict = self.mediaData[(NSUInteger)podcastId];
MGPlayState *pl_state = [[MGPlayState alloc] initWithPlayState:[[mediaDict objectForKey:#"playState"] integerValue]];
NSString *urlString = [mediaDict objectForKey:#"mediaLink"];
/// Finde Pathname
NSString *fileName = [urlString lastPathComponent];
NSLog(#"LastFileComponent: %#", fileName);
NSString *pathName = [NSString stringWithFormat:#"%#/%#",
[APFilePath getMediaContentFolder],
fileName];
/// Request und Operation
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:pathName
append:NO];
//// save Operation for cancle
NSMutableDictionary *operationDict = [[NSMutableDictionary alloc] init];
[operationDict setObject:operation
forKey:#"operation"];
[operationDict setObject:[NSNumber numberWithInt:podcastId]
forKey:#"myIndexPath"];
[operationDict setObject:[mediaDict objectForKey:#"mediaLink"]
forKey:#"mediaLink"];
[[self operationDictArr] addObject:operationDict];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSIndexPath *path = [NSIndexPath indexPathForRow:podcastId+1 inSection:0];
MGPodcastListBodyCell *myCell = (MGPodcastListBodyCell *) [self.podcastListTable cellForRowAtIndexPath:path];
[pl_state setToPlayState:PlayStateDefaultDownloadFinished];
myCell.cellPlayState = pl_state;
//============ Get mediaItem =============================
self.mediaData[(NSUInteger)podcastId][#"playState"] = #4;
/// remove operation from dict
[[self operationDictArr] removeObject:operationDict];
[self.podcastListTable reloadRowsAtIndexPaths:[self.podcastListTable indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
[self.podcastListTable setNeedsDisplay];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog (#"Error downloadMovie: %#", error);
}];
[operation start];
}
else
{
[EZToastView showToastMessage:NSLocalizedString(#"keineVerbindungKey", "")
withAlignment:EZToastViewAlignmentCenter];
}
}
Custom Cell:
//// MGPodcastListBodyCell.h
#protocol MGPodcastCellDelegate <NSObject>
#required
-(void) downloadButtonPressedOfCell: (NSInteger) podcastId;
-(void) cancleDownloadButtonPressedOfCell: (NSInteger) podcastId;
#end
#interface MGPodcastListBodyCell : UITableViewCell
#property (nonatomic, retain) id <MGPodcastCellDelegate> podcastCellDelegate;
#property (weak, nonatomic) IBOutlet UILabel *mediaTitleLabel;
#property (weak, nonatomic) IBOutlet UILabel *durationLabel;
#property (weak, nonatomic) IBOutlet UIButton *downloadMediaButton;
#property (weak, nonatomic) IBOutlet UIButton *cancelMediaDownloadButton;
#property (weak, nonatomic) IBOutlet MGProgressDownloadView *progressDownloadView;
#property (weak, nonatomic) IBOutlet UILabel *pubDateLabel;
#property (strong, nonatomic) MGPlayState *cellPlayState;
#property (nonatomic) NSInteger podcastId;
- (IBAction) downloadButtonPressed:(UIButton *)sender;
- (IBAction) cancleMediaDownloadButonPressed:(UIButton *)sender;
#end
//MGPodcastListBodyCell.m
#implementation MGPodcastListBodyCell
#synthesize cellPlayState = _cellPlayState;
- (void)setCellPlayState:(MGPlayState *) cellPlayState {
_cellPlayState = cellPlayState;
[self playStateChanged];
}
- (void)awakeFromNib {
[self setup];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
UIView *customBackgroundView = [[UIView alloc] init];
customBackgroundView.backgroundColor = [APAppearence sharedInstance].tableCellBackgroundColorMB;
self.backgroundView = customBackgroundView;
self.mediaTitleLabel.textColor = [APAppearence sharedInstance].tableCellMainlabelTextColorMB;
self.durationLabel.textColor = [APAppearence sharedInstance].standardDarkGrayColorMB;
self.tintColor = [APAppearence sharedInstance].tableCellMainlabelTextColorMB;
[self playStateChanged];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void) playStateChanged {
self.downloadMediaButton.hidden = self.cellPlayState.playButtonHidden;
[self.downloadMediaButton setNeedsDisplay];
self.cancelMediaDownloadButton.hidden = self.cellPlayState.cancelButtonHidden;
[self.cancelMediaDownloadButton setNeedsDisplay];
self.progressDownloadView.hidden = self.cellPlayState.progressViewHidden;
[self setNeedsDisplay];
}
- (IBAction) downloadButtonPressed:(UIButton *)sender {
[self.podcastCellDelegate downloadButtonPressedOfCell: self.podcastId];
}
- (IBAction) cancleMediaDownloadButonPressed:(UIButton *)sender {
[self.podcastCellDelegate cancleDownloadButtonPressedOfCell: self.podcastId];
}
#end
So if somebody can tell me, what to do more than reload the cell to update the View I would be very grateful. Thanks.
When you reload the cell you have code as follows...
MGPodcastListBodyCell *cell = (MGPodcastListBodyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierBodyCell];
if (cell == nil) {
....
}
In your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
method. Because the cell is being reused the cell won't be nil the second time around and therefore isn't being updated with any new information.
You need to do something when the cell is not nil to refresh it.
I found the bug. It wasn't a problem with the reloadRowAtIndexPath method. It was a concurrency problem. The download finish state got overwritten by the download progress thread just at the end of downloading and the state was set back to download.
So, thank you all for your help.
I have a Collection View Controller with Cells that get their content from a JSON call. Each Cell has a Scroll View (an image slideshow). I noticed that the slideshows are being loaded on top of the old ones (I could see the slideshow from the old cells appear right before the new slideshow is loaded). Or if I swipe to let's say the third image of my first cell and then scroll the fourth cell, it shows my the third image (instead of the first), but the Page Control will show that that's the first slide.
How can I "clear" the old cells (or at least clear the Scroll View, or stop it from being reused) while the new ones are being generated?
Article.h (NSObject)
#interface Article : NSObject
#property (readonly) NSURL *imageURL;
#property (nonatomic, strong) NSArray *images;
- (instancetype)initWithAttributes:(NSDictionary *)attributes;
+ (void)latestNewsWithBlock:(void (^)(NSArray *news, NSError *error))block;
#end
#pragma mark -
#interface ArticleImage : NSObject
#property (readonly, nonatomic, strong) NSURL *URL;
- (instancetype)initWithAttributes:(NSDictionary *)attributes;
#end
Article.m (NSObject)
#interface Article ()
#property (readwrite, nonatomic, strong) NSURL *URL;
#end
#implementation Article
- (instancetype)initWithAttributes:(NSDictionary *)attributes {
self = [super init];
if (!self) {
return nil;
}
self.URL = [NSURL URLWithString:attributes[#"url"]];
NSMutableArray *mutableImages = [NSMutableArray array];
for (NSDictionary *imageAttributes in attributes[#"photos"]) {
NSDictionary *imageFileAttributes = [imageAttributes valueForKeyPath:#"photo_file.photo_file"];
ArticleImage *image = [[ArticleImage alloc] initWithAttributes:imageFileAttributes];
[mutableImages addObject:image];
}
self.images = mutableImages;
return self;
}
- (NSURL *)imageURL {
return [[self.images firstObject] URL];
}
+ (void)latestNewsWithBlock:(void (^)(NSArray *news, NSError *error))block {
[[DeadstockAPIManager sharedManager] GET:#"articles" parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
NSMutableArray *mutableNews = [NSMutableArray array];
for (NSDictionary *attributes in JSON[#"articles"]) {
Article *news = [[Article alloc] initWithAttributes:attributes];
[mutableNews addObject:news];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (block) {
block([NSArray arrayWithArray:mutableNews], nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (block) {
block(nil, error);
}
}];
}
#end
#pragma mark -
#interface ArticleImage ()
#property (readwrite, nonatomic, strong) NSURL *URL;
#property (readwrite, nonatomic, strong) NSURL *thumbnailURL;
#end
#implementation ArticleImage
- (instancetype)initWithAttributes:(NSDictionary *)attributes {
self = [super init];
if (!self) {
return nil;
}
self.URL = [NSURL URLWithString:[attributes valueForKeyPath:#"thumb.url"]];
return self;
}
Collection View Controller
- (void)setLatestNews:(NSArray *)latestNews {
_latestNews = latestNews;
[self.collectionView reloadData];
}
- (void)loadData:(id)sender {
[Article latestNewsWithBlock:^(NSArray *news, NSError *error) {
self.latestNews = news;
}];
}
#pragma mark - UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[ArticleCell class] forCellWithReuseIdentifier:#"ArticleCell"];
[self loadData:nil];
}
#pragma mark - UICollectionViewDataSource
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"articleCell";
ArticleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.article = [self.latestNews objectAtIndex:indexPath.row];
return cell;
}
Collection View Cell
#interface ArticleCell ()
#property (readwrite, nonatomic, strong) NSArray *pageImages;
#end
#implementation ArticleCell
- (void)setArticle:(Article *)article {
_article = article;
self.pageImages = [self.article.images valueForKeyPath:#"URL"];
NSUInteger pageCount = [self.pageImages count];
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
self.pageControl.hidden = (pageCount == 1);
for (NSInteger page = 0; page < pageCount; page++) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
UIImageView *imageView = [[UIImageView alloc] init];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.frame = frame;
[imageView setImageWithURL:self.pageImages[page]];
[self.scrollView addSubview:imageView];
}
CGSize pagesScrollViewSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageImages.count, pagesScrollViewSize.height);
}
Thanks.
Example project can be found here: http://www.filedropper.com/collectionviewtest
Implement this as a public method in your ArticleCell:
- (void)cleanForReuse
{
[[self.scrollView subviews]
makeObjectsPerformSelector:#selector(removeFromSuperview)];
self.scrollView.contentSize = CGSizeZero;
}
- (void)prepareForReuse
{
[super prepareForReuse];
self.pageImages = nil;
}
Then update your code to call cleanForReuse before reusing the cell:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"articleCell";
ArticleCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:identifier
forIndexPath:indexPath];
[cell cleanForReuse];
cell.article = [self.latestNews objectAtIndex:indexPath.row];
return cell;
}
You're seeing this because the cells are being reused (which is a good thing for performance reasons).
When you have old content that will need to be removed from a cell when it gets reused, you will need to remove it in collectionView:cellForItemAtIndexPath:.
In your case, you will want to call removeFromSuperview on all of the imageViews that you added to the scrollView before adding the new imageViews.
You can also do some reuse preparation in the UICollectionViewCell method prepareForReuse. However, for performance reasons in UITableViewCells Apple recommends that "you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state.". Presumably, this same recommendation applies to UICollectionViewCells.
I'm seeing a gradual build up of memory that I think might be a retain cycle.
When does this happen: Click on a custom cell that expands and injects a nib with 3 buttons into the expanded area. Clicking the cell again closes the cell, shrinks the cell's tablerow height, rotates an open indicator and removes the previously injected nib.
If I open and close the cell multiple times I see the memory gradually building up.
Any ideas what might be causing this would be greatly appreciated.
Sorry I don't have enough reputation to post photos.
Build up:
Example of retained objects(mostly Animation related):
EDIT: Using ARC and on iOS 6
MasterViewController - TableView Functions
#pragma mark - UITABLEVIEW
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.topicsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier2 = #"SRCollapsibleCellClosed";
SRCollapsibleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if (cell == nil) {
cell = [[SRCollapsibleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier2];
}
SRTopic *topic = [self.topicsArray objectAtIndex:indexPath.row];
[cell updateWithTopic:topic];
if([self isCellOpen:indexPath]){
CGAffineTransform transformation = CGAffineTransformMakeRotation(M_PI/2);
cell.arrow.transform = transformation;
if(![self hasChoiceBox:cell]){
[self insertChoiceBox:cell atIndex:indexPath];
}
} else{
CGAffineTransform transformation = CGAffineTransformMakeRotation(0);
cell.arrow.transform = transformation;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([self isCellOpen:indexPath]){
[self closeCellAtIndexPath:indexPath];
}
else{
NSIndexPath * openCell= self.openCellIndex;
NSIndexPath * newOpenCell= indexPath;
[self closeCellAtIndexPath:openCell];
[self openCellAtIndexPath:newOpenCell];
}
[tableView beginUpdates];
[tableView endUpdates];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
-(CGFloat)tableView: (UITableView*)tableView heightForRowAtIndexPath: (NSIndexPath*) indexPath {
if([indexPath isEqual:self.openCellIndex]){
return 217.0;
} else {
return 63.0;
}
}
-(void)rotateCellArrowAtIndexPath:(NSIndexPath*)indexPath willOpen:(bool)willOpen Animated:(bool)animated{
// Change Arrow orientation
SRCollapsibleCell *cell = (SRCollapsibleCell*) [self.topicsTableView cellForRowAtIndexPath:indexPath];
CGAffineTransform transformation;
if(willOpen){
transformation = CGAffineTransformMakeRotation(M_PI/2);
} else {
transformation = CGAffineTransformMakeRotation(0);
}
if(animated){
[UIView animateWithDuration:.2 delay:0 options:nil animations:^{
cell.arrow.transform = transformation;
} completion:nil];
}
else{
cell.arrow.transform = transformation;
}
}
-(BOOL)isCellOpen:(NSIndexPath *)indexPath{
return [indexPath isEqual:self.openCellIndex];
}
-(void)closeCellAtIndexPath:(NSIndexPath*)indexPath{
//NSLog(#"Cell closing");
[self rotateCellArrowAtIndexPath:indexPath willOpen:NO Animated:YES];
[self removeSRChoiceBoxFromCellAtIndexPath:indexPath];
self.openCellIndex = nil;
}
-(void)openCellAtIndexPath:(NSIndexPath*)indexPath{
[self rotateCellArrowAtIndexPath:indexPath willOpen:YES Animated:YES];
SRCollapsibleCell *cell = (SRCollapsibleCell*)[self.topicsTableView cellForRowAtIndexPath:indexPath];
[self insertChoiceBox:cell atIndex:indexPath];
self.openCellIndex = indexPath;
}
-(void)removeSRChoiceBoxFromCellAtIndexPath:(NSIndexPath *)indexPath{
SRCollapsibleCell *cell = (SRCollapsibleCell*) [self.topicsTableView cellForRowAtIndexPath:indexPath];
for(id subview in cell.SRCollapsibleCellContent.subviews){
if([subview isKindOfClass:[SRChoiceBox class]]){
SRChoiceBox *tempBox = subview;
[tempBox removeFromSuperview];
tempBox.delegate = nil;
tempBox = nil;
}
}
}
-(void)insertChoiceBox: (SRCollapsibleCell *)cell atIndex:(NSIndexPath *) indexPath
{
//SRChoiceBox *newBox = [[SRChoiceBox alloc] initWithFrame:CGRectMake(0, 0, 310, 141)];
SRChoiceBox *newBox = [[SRChoiceBox alloc] init];
SRTopic *topic = [self.topicsArray objectAtIndex:indexPath.row];
[newBox updateWithSRTopic:topic];
newBox.delegate = self;
[cell.SRCollapsibleCellContent addSubview:newBox];
cell = nil;
topic = nil;
newBox = nil;
}
-(bool)hasChoiceBox:(SRCollapsibleCell *)cell{
for(UIView *subview in cell.SRCollapsibleCellContent.subviews){
if([subview isKindOfClass:[SRChoiceBox class]]){
return true;
}
}
return false;
}
SRChoiceBox - UIView object that gets inserted into cell
//.h
#protocol SRChoiceBoxDelegate <NSObject>
-(void)positionWasChoosen: (NSString *)choice topicId: (NSNumber *)topicId;
#end
#interface SRChoiceBox : UIView
-(id) initWithLabel: (NSDictionary *)labels andTopicID: (NSNumber *)topicId andFrame:(CGRect)frame;
#property (nonatomic, weak) IBOutlet UIView *SRChoiceBox;
#property (nonatomic, strong) NSNumber *SRTopicId;
#property (nonatomic, weak) id<SRChoiceBoxDelegate> delegate;
#property (weak, nonatomic) IBOutlet UILabel *agreeCount;
#property (weak, nonatomic) IBOutlet UILabel *disagreeCount;
#property (weak, nonatomic) IBOutlet UILabel *observeCount;
-(IBAction)buttonPress:(id)sender;
-(void)updateWithSRTopic:(SRTopic *)topic;
....
//.m
-(id)init{
self = [super init];
if (self) {
UINib *nib = [UINib nibWithNibName:#"SRChoiceBox" bundle:nil];
NSArray *q = [nib instantiateWithOwner:self options:nil];
[self addSubview:q[0]];
}
return self;
}
-(void)updateWithSRTopic:(SRTopic *)topic
{
self.SRTopicId = topic.topicId;
self.agreeCount.text = [NSString stringWithFormat: #"%#",topic.agreeDebaters];
self.disagreeCount.text = [NSString stringWithFormat: #"%#",topic.disagreeDebaters];
self.observeCount.text = [NSString stringWithFormat: #"%#",topic.observers];
}
- (IBAction)buttonPress:(id) sender {
int tag = [sender tag];
switch (tag) {
case 0:
[self.delegate positionWasChoosen:#"agree" topicId:self.SRTopicId];
break;
case 1:
[self.delegate positionWasChoosen: #"disagree" topicId:self.SRTopicId];
break;
case 2:
[self.delegate positionWasChoosen: #"observe" topicId:self.SRTopicId];
break;
default:
break;
}
}
- (void)dealloc
{
self.SRChoiceBox =nil;
self.SRTopicId=nil;
self.delegate=nil;
self.agreeCount=nil;
self.disagreeCount=nil;
self.observeCount=nil;
//NSLog(#"choicebox deallocated: %#", self);
}
SRCollapsibleCell -- Reusable cell
//.h
#interface SRCollapsibleCell : UITableViewCell
#property (strong) NSNumber *topicId;
#property (strong) NSDictionary *topicStats;
#property (weak, nonatomic) IBOutlet UILabel *title;
#property (weak, nonatomic) IBOutlet UILabel *subtitle;
#property (weak, nonatomic) IBOutlet UILabel *agreeDebaters;
#property (weak, nonatomic) IBOutlet UILabel *disagreeDebaters;
#property (weak, nonatomic) IBOutlet UILabel *observers;
#property (weak, nonatomic) IBOutlet UIImageView *arrow;
#property (weak, nonatomic) IBOutlet UIView *SRCollapsibleCellContent;
//-(void)updateWithTopic:(NSDictionary *) stats;
-(void)formatTitle:(NSString *)title;
-(void)updateWithTopic: (SRTopic *)topic;
#end
//.m
#implementation SRCollapsibleCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
-(void)formatTitle:(NSString *)title{
if(title.length<30){
self.title.text= title;
self.subtitle.text =#"";
} else {
NSArray *splitString = [self splitString:title maxCharacters:30];
self.title.text = splitString[0];
self.subtitle.text = splitString[1];
splitString = nil;
title = nil;
}
}
////http://www.musicalgeometry.com/?p=1197
- (NSArray *)splitString:(NSString*)str maxCharacters:(NSInteger)maxLength {
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:1];
NSArray *wordArray = [str componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSInteger numberOfWords = [wordArray count];
NSInteger index = 0;
NSInteger lengthOfNextWord = 0;
while (index < numberOfWords && tempArray.count<2) {
NSMutableString *line = [NSMutableString stringWithCapacity:1];
while ((([line length] + lengthOfNextWord + 1) <= maxLength) && (index < numberOfWords)) {
lengthOfNextWord = [[wordArray objectAtIndex:index] length];
[line appendString:[wordArray objectAtIndex:index]];
index++;
if (index < numberOfWords) {
[line appendString:#" "];
}
}
[tempArray addObject:line];
NSMutableString *subtitle = [NSMutableString stringWithCapacity:1];
while(index<numberOfWords){
[subtitle appendString:[wordArray objectAtIndex:index]];
[subtitle appendString:#" "];
index++;
}
[tempArray addObject:subtitle];
break;
}
return tempArray;
}
//Breaks MVC but it makes the MasterVC cleaner
-(void)updateWithTopic: (SRTopic *)topic
{
[self formatTitle:topic.title];
self.topicId = topic.topicId;
self.agreeDebaters.text = [NSString stringWithFormat:#"%#",topic.agreeDebaters];
self.disagreeDebaters.text = [NSString stringWithFormat:#"%#", topic.disagreeDebaters];
self.observers.text = [NSString stringWithFormat:#"%#", topic.observers];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
#end