I'm trying to implement a UIScrollView in each cell of `UITableView'. I wrote my code based on Apple's custom Scrollview project: https://developer.apple.com/library/ios/#samplecode/Scrolling/Listings/ReadMe_txt.html
Each table cell's UIScrollView scrolls through a set of labels, which are initialized in the cellForRowAtIndexPath method. Each label's text is set after initialization to equal a string from an array.
Everything works correctly until I scroll down to the 4th table cell. The UIScrollView in this cell has the same exact labels as the first cell. The same first 3 set of labels or the first 3 scrollViews keep repeating after the first 3 cells. The strange part is, when I log the label.text after it is set, the output shows the correct label.text of what should be displaying in the respective cell.
- (void)layoutScrollLabels: (float)arrayCount
{
UIView *view = nil;
NSArray *subviews = [cell.popularLinkScrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UILabel class]] && view.tag >= 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[cell.popularLinkScrollView setContentSize:CGSizeMake((arrayCount * kScrollObjWidth), [cell.popularLinkScrollView bounds].size.height)];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Custom Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *dictionary = [parseDataArray objectAtIndex:indexPath.row];
NSArray *popularLinkTitleArray = [dictionary objectForKey:#"popularLinkTitleArray"];
cell.popularLinkScrollView.clipsToBounds = YES;
kScrollObjHeight = cell.popularLinkScrollView.frame.size.height;
kScrollObjWidth = cell.popularLinkScrollView.frame.size.width;
NSUInteger i;
for (i = 0; i < popularLinkTitleArray.count; i++)
{
NSString *string = [NSString stringWithFormat:#"%#", [popularLinkTitleArray objectAtIndex: i]];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:#"%#", string];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 5;
NSLog(#"%#", label.text);
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = label.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
label.frame = rect;
label.tag = i; // tag our images for later use when we place them in serial fashion
[cell.popularLinkScrollView addSubview:label];
}
[self layoutScrollLabels:popularLinkTitleArray.count];
}
return cell;
}
You should not add label as a subview to popularLinkScrollView in cellForRowAtIndexPath. If the count of labels is required to be dynamic, try this code before your for loop in cellForRowAtIndexPath.
for (UIView* subView in cell.popularLinkScrollView.subviews)
[subView removeFromSuperview];
Related
I add UIView in UITableviewcell. The first occurrence of all appears normal. But every time when you scroll all the strays. UILabel overlap with different values. Below is the code to create Cell. What am I doing wrong?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
float low_bound = 0;
float high_bound = 7;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);
int dayStart = rndValue;
int dayMonth = 30;
int maxI = (dayStart+(7-dayStart))*(indexPath.row+1);
int Pos;
if (indexPath.row == 0) {
dayNumber = dayStart;
Pos = 0;
} else {
Pos = dayNumber;
}
for (int i=dayNumber; i < maxI; i++) {
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(4, 4, 40, 40)];
if (i > 0) {
CGRect frame = myView.frame;
frame.origin.x = (44.0*(i- Pos-1));
myView.frame = frame;
}
UILabel *yourLabel = [[UILabel alloc] initWithFrame:CGRectMake(24, 0, 40, 40)];
[yourLabel setText:[NSString stringWithFormat:#"%i", i-dayStart+1]];
[myView addSubview:yourLabel];
[cell addSubview:myView];
dayNumber++;
}
return cell;
}
THe cells you are using are being reused by the system, and every tieme that happens, you just slap another label on top of all the others. You need to either remove the labels before reusing the cell by doing something like
//this needs to be placd before the for-loop
for (UIView *view in cell.contentView.subViews){
[view removeFromSuperview];
}
or you can keep references to the labels and just assign new values to them.
I would advise you to do the latter, but it will probably mean that you have to subclass UITableViewCell.
Right now, the code in your forloop keeps creating new labels and just stacks them up like a lasagna.
I have requirement to scroll the images horizontally and vertically.
I have sections which will segregated vertically and in each section itself it will be so many images which will be scrolled horizontally.
I made vertical scrolling using UICollectionView with Ray wenderlich tutorial but how do I get horizontal scrolling in each section?
Should I use any other object like UITableView or UIScrollview for this purpose?
Any tutorial available how to achieve this?
Is it compulsory to implement Layout subclass or UICollectionviewFlowlayout when using collection view?
EDIT
Till now I have implemented this code which scrolls vertically and has 5 sections each but not able to scroll the individual sections
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;
{
NSArray *arrayReturn = [self returnArray:section];
return arrayReturn.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ImageCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"imagecell" forIndexPath:indexPath];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSArray *arrSection = [NSArray arrayWithArray:[self returnArray:indexPath.section]];
NSString *fileName = [arrSection objectAtIndex:indexPath.row];
UIImage *image = [UIImage imageNamed:fileName];
dispatch_async(dispatch_get_main_queue(), ^{
// cell.imgVw.image = ;
if([CollectionView.indexPathsForVisibleItems containsObject:indexPath]){
ImageCell *currentCell = (ImageCell *) [cv cellForItemAtIndexPath:indexPath];
currentCell.imgVw.image = image;
}
});
}];
[self.thumbnailQueue addOperation:operation];
return cell;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
HeaderView *Header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"PhotoHeaderView" forIndexPath:indexPath];
Header.lblHeaderTitle.text = #"Header title";
Header.backgroundColor = [UIColor yellowColor];
return Header;
}
Here is a similar tutorial:
http://www.raywenderlich.com/4723/how-to-make-an-interface-with-horizontal-tables-like-the-pulse-news-app-part-2
Basically you need to do following:
Take a tableView or CollectionView
In each cell you need to add a scrollview with horizontal scrolling enabled.
Add items to scroll view of each cell while creating cells and give the scroll view appropriate content size.
It will give you a desired result.
Try this
**viewController.h**
IBOutlet UIScrollView *scrollViewMain;
**viewController.m**
- (void)viewDidLoad
{
[super viewDidLoad];
[self createDisplay];
// Do any additional setup after loading the view, typically from a nib.
}
-(void) createDisplay
{
for(UIView *subView in scrollViewMain.subviews)
{
if(![subView isKindOfClass:[UIImageView class]])
{
[subView removeFromSuperview];
}
}
int yPos =5;
for (int i=0; i< 10; i++)
{
int xPosition=5;
UIScrollView *scrollView =[[UIScrollView alloc]initWithFrame:CGRectMake(xPosition, yPos, scrollViewMain.frame.size.width, 100)];
scrollView.backgroundColor =[UIColor clearColor];
scrollView.autoresizingMask =UIViewAutoresizingFlexibleWidth;
xPosition +=5;
for (int j=0; j<10; j++)
{
UILabel *lblTitle = [[UILabel alloc] initWithFrame:CGRectMake(xPosition, 0, 100, 100)];
lblTitle.textColor = [UIColor whiteColor];
lblTitle.backgroundColor =[UIColor greenColor];
lblTitle.text = #"test";
lblTitle.numberOfLines =0;
lblTitle.font = [UIFont boldSystemFontOfSize:15];
[scrollView addSubview:lblTitle];
xPosition +=100;
xPosition +=10;
}
[scrollView setContentSize:CGSizeMake(xPosition, 100)];
[scrollViewMain addSubview:scrollView];
yPos +=120;
}
[scrollViewMain flashScrollIndicators];
[scrollViewMain setContentSize:CGSizeMake(0, yPos)];
}
I have a custom cell designed with a scrollview and a pageview control, which I am displaying as follows
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * CellIdentifier = #"ScrollViewCell";
cell = (ScrollViewCell*)[newsTable dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray * customcellArray = [[NSBundle mainBundle]loadNibNamed:#"ScrollViewCell" owner:self options:nil];
for(id customcellObject in customcellArray){
if([customcellObject isKindOfClass: [UITableViewCell class]]){
cell = (ScrollViewCell *)customcellObject;
break;
}
}
}
// Customize your UIScrollView here..
[cell.scrollView setDelegate:self];
[cell.scrollView setPagingEnabled:YES];
scrollView = cell.scrollView;
pageControl = cell.pageControl;
cell.backgroundColor = [UIColor grayColor];
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor],[UIColor blueColor], nil];
cell.scrollView.contentSize = CGSizeMake(cell.scrollView.frame.size.width * colors.count,cell.scrollView.frame.size.height);
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = cell.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = cell.scrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[cell.scrollView addSubview:subview];
}
// Configure the cell...
UIView* bgview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
bgview.opaque = YES;
bgview.backgroundColor = [UIColor grayColor];
[cell setBackgroundView:bgview];
return cell;
}
The scrollview appears and scrolls just fine in cell , but problem is page control doesnt update with the scroll, basically I want to update the page control on scroll of scrollview but since page control and scrollview both are from cell I am not getting how to achieve this, I tried implementing UIScrollViewDelegate protocol with cell and then the parent view of table view but couldnt get it working, pleas guide.
Thanks
Vishal
Don't place your UIPageControll over UIScrollview in your custom cell, if it is then it will get scroll along UIScrollView itself.So create your Custom cell like this and make outlet for each UIScrollview & UIPageControll,
One change in your cellForRowAtIndexPath
// Customize your UIScrollView here..
[cell.scrollView setDelegate:self];
[cell.scrollView setPagingEnabled:YES];
[cell.scrollView setTag:indexPath.row]; // set indexpath.row as tag for cell.scrollview
NSArray * colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor],[UIColor blueColor],nil];
cell.pageControll.numberOfPages = [colors count];
And in,
- (void)scrollViewDidScroll:(UIScrollView *)sender {
CGFloat xOffset = sender.contentOffset.x;
CGFloat frameWidth = sender.frame.size.width;
int page = floor((xOffset - frameWidth / 2) / frameWidth) + 1;
GroupButtonCell * cell = (GroupButtonCell*)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:sender.tag inSection:0]]; // get appropriate cell based on scrollview tag (sender.tag).
cell.pageControll.currentPage = page; // assigning to pagecontroll
}
Hope this will help..
I have a UITableView and a UITableCell subclass. Each table cell has two scrollviews that each rotate a dynamic amount of labels, which are created in my table cell implementation. I have poor scrolling performance and I believe it is from memory leaks. I'm referencing this stackoverflow correct answer to fix my problem: cellForRowAtIndexPath memory management.
I can't figure out how to tweak my code so I don't have to allocate memory every time I create labels.
ViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Custom Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *dictionary = [parseDataArray objectAtIndex: indexPath.row];
NSArray *popularLinkTitleArray = [dictionary objectForKey:#"popularLinkTitleArray"];
NSString *soundCloudLink = [dictionary objectForKey:#"soundCloudLink"];
NSArray *soundCloudTrackTitleArray = [dictionary objectForKey:#"soundCloudTrackTitleArray"];
cell.artistNameLabel.text = artistName;
[cell createPopularLinkLabels: popularLinkTitleArray];
return cell;
}
CustomCell.m
- (void)layoutScrollLabelsForPopularLinkScrollView: (float)arrayCount
{
UIView *view = nil;
NSArray *subviews = [popularLinkScrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UILabel class]] && view.tag >= 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[popularLinkScrollView setContentSize:CGSizeMake((arrayCount * kScrollObjWidth), [popularLinkScrollView bounds].size.height)];
}
-(void) createPopularLinkLabels:(NSArray *) popularLinkTitleArray
{
popularLinkScrollView.clipsToBounds = YES;
kScrollObjHeight = popularLinkScrollView.frame.size.height;
kScrollObjWidth = popularLinkScrollView.frame.size.width;
for (UIView* subView in popularLinkScrollView.subviews)
[subView removeFromSuperview];
NSUInteger i;
for (i = 0; i < popularLinkTitleArray.count; i++)
{
NSString *string = [NSString stringWithFormat:#"%#", [popularLinkTitleArray objectAtIndex: i]];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:#"%#", string];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 5;
[label setFont:[UIFont fontWithName:#"Calibri" size:18]];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = label.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
label.frame = rect;
label.tag = i; // tag our images for later use when we place them in serial fashion
[popularLinkScrollView addSubview:label];
}
[self layoutScrollLabelsForPopularLinkScrollView:popularLinkTitleArray.count];
}
Are you using ARC? If not, you should autorelease to
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]
Cell objects are reused and you do not need to re-add subviews each time you change the contents. Just change the data and update the label frames if needed.
If you have distinctly different cells, consider using separate custom cell classes. You will need to use a separate value for the reuseIdentifier.
Also consider not creating a custom cell but just adding elements to the standard UITableViewCell. Read this about styling a cell.
The first thing you could do is try to reuse UILabels in createPopularLinkLabels. Right now, you are just removing them before adding more.
But if the number of popular links is not the same in all the cells, then you will have to remove the remaining ones or hide them and there will be no way to avoid memory allocations.
A better solution would be to insert a custom UIView inside your scroll view and draw all the texts manually instead of creating tons of subviews.
Hope this helps,
Thomas
Okay I am just totally too close to this I think. I have a tableview. In that table view everything is working except section 2 row 0.
AND -- before anyone asks...I know I am not using the *imgWordView (in the code below) and that is why the icons are not showing but i do not know where to add it to the subview so i can place it at coordinates in the cell... :)
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
if (indexPath.section == 2 && indexPath.row == 0)
{
cell = [self getCellContentViewForIconRow:CellIdentifier];
cell.backgroundColor = [UIColor grayColor];
}
else
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
}
}
The above code (except for my addition of of my section == 2 && indexPath.row == 0) is normal and generic..
getCellContentViewForIconRow is defined here:
- (UITableViewCell *) getCellContentViewForIconRow:(NSString *)cellIdentifier {
ResortsListViewController *resortsListViewController = [[ResortsListViewController alloc] init];
NSMutableArray *categoryArray = [resortsListViewController getCategoriesForLocation:[[locationData objectForKey:#"theid"]intValue]];
[resortsListViewController release];
CGRect CellFrame = CGRectMake(0, 0, 260, 60);
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:cellIdentifier] autorelease];
cell.contentView.backgroundColor = [UIColor grayColor];
int x = 10;
int y = 10;
for (NSMutableDictionary *cat_object in categoryArray) {
CGRect Label1Frame = CGRectMake(x, y, x + 40, 40);
UILabel *lblTemp = [[UILabel alloc] initWithFrame:Label1Frame];
lblTemp.tag = x + 1;
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"alcohol.png"];
UIImage *imgWord = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView *imgWordView = [[[UIImageView alloc] initWithImage:imgWord] autorelease];
[cell.contentView addSubview:lblTemp];
[lblTemp release];
x = x + 30;
}
return cell;
}
Okay, so
ResortsListViewController *resortsListViewController = [[ResortsListViewController alloc] init];
NSMutableArray *categoryArray = [resortsListViewController getCategoriesForLocation:[[locationData objectForKey:#"theid"]intValue]];
[resortsListViewController release];
gets back a group of categories that I would use to build the filenames for the PNG images. Everything is okay so far. Then I am looping through the array to build the places for the images in the cell.
I am expecting no more than 5 images...at 30x30 each. so, to test, I just used the filename "alcohol.png" which exists. I want all 5 images to be displayed in a row inside the cell
I already do this:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 2 && indexPath.row == 0)
{
return 60;
}
}
But, all I see is my table cell (in gray like it is supposed to be) with a LONG white box inside it.
So, two questions.
1: The box (i am assuming it is the CGRect CellFrame = CGRectMake(0, 0, 260, 60);) in white. I need to know how to change that to gray...just like my cell background.
The big reason is that my icons are white...and i cannot tell if the icons are visible or not.
If the only problem is that this white CGRECT is hiding my icons, then all is well.
Otherwise we come to #2:
2: Is this the best way to put a group of icons within a cell row when the icon names are inside an array?
If not, how?
So, How do i make the CGRect gray & how do i get the 5 icons to appear horizontally in the cell?
Thanks in advance for your help!
The big long white box is your labels you are making and adding to the contentView. The UILabels are opaque with a white background. Do the following:
lblTemp.backgroundColor = [UIColor clearColor];
This will get rid of the white box so you can see what is behind your labels.
The other issue is that you you are adding your x + 40 on every iteration of your loop. I would think you would want the width to be a constant value and not adding your X coord + 40 each time, this will end up making a pretty wide set of labels across your cell.
Okay, found out how to do this elegantly and with a flair. I am voting for #bstahlhood's answer as the answer as it is partially what I needed to know..however, this subroutine in place of the one I submitted above is how I fully fixed it.
- (UITableViewCell *) getCellContentViewForIconRow:(NSString *)cellIdentifier {
ResortsListViewController *resortsListViewController = [[ResortsListViewController alloc] init];
NSMutableArray *categoryArray = [resortsListViewController getCategoriesForLocation:[[locationData objectForKey:#"theid"]intValue]];
[resortsListViewController release];
MainAppDelegate *mainAppDelegate = [[MainAppDelegate alloc] init];
CGRect CellFrame = CGRectMake(10, 10, 100, 40);
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:cellIdentifier] autorelease];
int x = 10;
int y = 10;
int ctr = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *category_name in categoryArray) {
if (ctr <= 7)
{
BOOL valid_for_user = [mainAppDelegate validate_category_for_users_level:category_name];
if (valid_for_user)
{
NSMutableString *subt = [[[NSMutableString alloc]init]autorelease];
[subt appendString: category_name];
[subt appendString: #".png"];
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithString:subt]];
BOOL success = [fileManager fileExistsAtPath:filePath];
if (success)
{
UIImage *imgWord = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView *imgWordView = [[[UIImageView alloc] initWithImage:imgWord] autorelease];
CGRect Label1Frame = CGRectMake(x, y, 30, 30);
UILabel *lblTemp = [[UILabel alloc] initWithFrame:Label1Frame];
lblTemp.tag = x + 1;
// BEGIN FIX TO MY PROBLEM: thanks #bstahlhood
lblTemp.backgroundColor = [UIColor clearColor];
// END FIX TO MY PROBLEM
[lblTemp addSubview:imgWordView];
[cell.contentView addSubview:lblTemp];
[lblTemp release];
x = x + 40;
ctr += 1;
}
}
}
}
[mainAppDelegate release];
return cell;
}
As you see I also changed the CGRECT inside my loop to fix the horizontal spacing:
CGRect Label1Frame = CGRectMake(x, y, 30, 30);
as #bstahlhood suggested. I had forgotten CGRrect commands handle the startpoints and width instead of startpoints and endpoints inside the cell area. that is why I thought I had to use x+40. As stated, it is now corrected.
I also changed this the heightForRowAtIndexPath to better balance the look of a 30x30 pixel image. The label above starts my subview 10px across and 10px down from the inside of the cell, so it just makes sense to ensure that the cell is 50px vertically and not 60px so there is exactly 10px on top and 10px on bottom of the icon.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 2 && indexPath.row == 0)
{
return 50;
}
}
So, even though I solved my own problem prior to the posting by #bstahlhood, I am giving him the vote for the correct answer as it was correct. I also wanted to point out that I wish more people would post fixed code after their issue was corrected so that others having the same issue would know what (exactly) they did. That is why I posted this code.