UICollectionView header position in horizontal scroll direction mode with flow layout - ios

I have ios UICollectionView with Flow layout with horizontal scroll direction.
In this situation typical header position is on the left side of cells.
I want to make header on the top of section cells
Have you any idea how can I do this?

I solved the problem using DateFlowLayout.
See how I configured it in this other answer.

I think you can get what you need using standard behavior for the header.
Set it up (probably in viewDidLoad):
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.headerReferenceSize = CGSizeMake(self.collectionView.bounds.size.width, 30);
// other setup
[self.collectionView setCollectionViewLayout:flowLayout];
Then answer a header view:
#define MY_HEADER_LABEL_TAG 128
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:
UICollectionElementKindSectionHeader withReuseIdentifier:#"SectionHeader" forIndexPath:indexPath];
UILabel *label = (UILabel *)[headerView viewWithTag:MY_HEADER_LABEL_TAG];
if (!label) {
label = [[UILabel alloc] initWithFrame:CGRectInset(headerView.bounds, 5, 5)];
label.tag = MY_HEADER_LABEL_TAG;
label.font = [UIFont boldSystemFontOfSize:12];
label.textColor = [UIColor darkGrayColor];
[headerView addSubview:label];
}
label.text = [NSString stringWithFormat:#"Section %d", indexPath.section];
return headerView;
}

Related

How to change the width of tableView cell textLabel

I want to add 2 imageViews on the Left and Right and a cell textLabel on the Center on TableView.
The 2 imageViews has different role and action.
(Being able to touch, hidden and not hidden)
Now the left side imageView is on the top of the center textLabel.
So I want to change the textLabel's width and x position not to be on the top of imageViews.
I tried this code but it didn't work.
CGRect frame = CGRectMake(40, 0, 200, 40);
cell.textLabel.frame = frame;
and I also tried to add UILabel on the tableView.
But I will implement TableViewCell animation and if I add UILabel, it doesn't work.
So I don't use this way.
How can I solve it?
here is the code.
- (void)viewDidLoad {
tableView = [[UITableView alloc]initWithFrame:rect style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.view addSubview: self.tableView];
self.tableView.backgroundColor = [UIColor clearColor];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self updateCell:cell atIndexPath:indexPath];
return cell;
}
- (void)updateCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *set = [self.section objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor blueColor];
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont fontWithName:#"HiraKakuProN-W3" size:15];
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.minimumScaleFactor = 10.0f;
cell.textLabel.numberOfLines = 0;
}
you need to override the layoutSubviews for your UItableviewCell class
- (void)layoutSubviews {
[super layoutSubviews];
CGSize size = self.bounds.size;
self.textLabel.frame = CGRectMake(2.0f, 4.0f, size.width, size.height); // or customize
self.textLabel.contentMode = UIViewContentModeScaleAspectFit;
}
Choice-2
crate the UILabel and subview to cell.contentView
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(4, 4, 200, 30)];
label.text = #"Test";
[cell.contentView addSubview:label];
Update
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self updateCell:cell atIndexPath:indexPath];
if (cell == nil){
myCellView = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"Cell"];
UILabel *orderLbl = [[UILabel alloc] initWithFrame:CGRectMake(1, 13, 176, 21)];
[orderLbl setTag:1];
orderLbl.textColor = [UIColor whiteColor];
[orderLbl setBackgroundColor:[UIColor clearColor]];
[orderLbl setTextAlignment:NSTextAlignmentCenter];
[cell.contentView addSubview:orderLbl];
}
[(UILabel *)[cell.contentView viewWithTag:1] setText:[NSString stringWithFormat:#"test"]];
return cell;
}
Better you can use Custom Textlabel with your desired frame requirement.
Give a tag for the textlabel and find out the label in cellForRowAtIndexPath as following :
UILabel *myLabel = (UILabel *)[cell.contentView viewWithTag:givenTagValue];
Then apply your required behaviour for this label as you want.
Hope it helps..
In such cases I prefer creating custom subclass and apply layout as I wanted so if I get any autolayout warning I always know it's because layout is not correct.
Actually, changing the frame the textLabel in cell is NOT allowed in TableView. The width of frame for the textlabel of each cell relates to the cell, developer couldn't set it programmatically. However, I worked out a feasible solution: we may not change the frame of the textLabel, but we may change the source text alternatively. If we can truncate the source string in advance, this problem is overcome.
//Assign each element in the array a row in table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var myCell = myTableView.dequeueReusableCell(withIdentifier: "The Cell")
if myCell == nil {
myCell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "The Cell")
}
myCell?.accessoryType = UITableViewCellAccessoryType.none
// If the text is too long (longer than 25 chars), the text will be truncated
// The lenght of label cannot be set effectively here, so I truncate the source string alternatively
var myString: String = sectionArray[indexPath.row]
if myString.characters.count > 25 {
let myIndex = myString.index(myString.startIndex, offsetBy: 25)
myString = myString.substring(to: myIndex)
myString += "..."
}
myCell?.textLabel?.text = myString
myCell?.detailTextLabel?.text = "\(NSDate())"
return myCell!
}
That supports to work.

Fill UICollectionViewCells in UICollectionView with data from an array programmatically

I have a UICollectionView which I would like to populate it with 100 UICollectionViewCells each with its own unique UILabel with text fetched from an array. I'd like to do this programmatically (without the Storyboard).
I've attempted it as posted below, but for some reason only the first cell renders properly.
// Setting up the UICollectionView
- (void)setupCollectionView {
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
CGRect newFrame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height * 0.5);
self.collectionView=[[UICollectionView alloc] initWithFrame:newFrame collectionViewLayout:layout];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[self.collectionView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:self.collectionView];
}
//Trying to generate the unique cells with data
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor = [UIColor yellowColor];
UILabel *label = [[UILabel alloc] initWithFrame: cell.frame];
label.text = [self.array objectAtIndex: indexPath.row];
label.textColor = [UIColor blackColor];
[cell.contentView addSubview:label];
return cell;
}
To note: my array is of size 100 with randomly generated numbers.
Your help is appreciated :)
Thanks!
The array's frame should be related to the bounds of its cell, not its cell's frame, who's origin will grow as the indexPath grows. Also, we don't want to unconditionally create the labels, since the cells get reused. Only create a label if one doesn't exist....
UILabel *label = (UILabel *)[cell viewWithTag:99];
if (!label) {
label = [[UILabel alloc] initWithFrame: cell.bounds]; // note use bounds here - which we want to be zero based since we're in the coordinate system of the cell
label.tag = 99;
label.text = [self.array objectAtIndex: indexPath.row];
label.textColor = [UIColor blackColor];
[cell.contentView addSubview:label];
}
// unconditionally setup its text
NSNumber *number = self.myArrayOfRandomNumbers[indexPath.row];
label.text = [number description];

Prevent content in each UICollectionViewCell in UICollectionView from changing as I scroll?

As I scroll back and forth in the UICollectionView, I notice that the content changes in the cells that appear and disappear on the screen.
Is there any way to prevent this from happening?
I just want to load all of the content to each cell appropriately and then prevent it from changing as I scroll.
Here's some of the code related to this:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor = [UIColor clearColor];
UILabel *label = (UILabel *)[cell viewWithTag:100];
if (!label) {
label = [[UILabel alloc] initWithFrame:cell.bounds];
label.tag = 100;
label.text = [self.array objectAtIndex:indexPath.row];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor blackColor];
[cell.contentView addSubview:label];
}
return cell;
}
The problem is that you are not supplying the content for the label in your cellForRowAtIndexPath:. Remember, these cells are being reused. So even if the label already exists, you need to supply the label text, because the cell is now being used in a different row and therefore needs different text!
So just move this line:
label.text = [self.array objectAtIndex:indexPath.row];
...down and outside the if block, to just before you return the cell.

UICollectionView Headers Overlapping

I have a collectionView with the following relevant methods:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
Scoreboard *thisScoreboard = [self.scoreboards objectAtIndex:indexPath.section];
ScoreboardReusableView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"
forIndexPath:indexPath];
//view.backgroundColor = [UIColor yellowColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)];
//label.text = [NSString stringWithFormat:#"%# vs %#", thisScoreboard.awayteam, thisScoreboard.hometeam];
label.text = [NSString stringWithFormat:#"%ld", (long)indexPath.section];
[view addSubview:label];
return view;
}
return nil;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeMake(self.view.frame.size.width, 34);
}
However, the label text is overlapping. Here are two screenshots of the collectionView. Sections 0 and 1 (notice how the text does NOT overlap):
And sections 2 and 3 (the text overlaps here and on all subsequent section headers):
My header xib ScoreboardReusableView is COMPLETELY blank because no matter what I put in it (e.g., a UILabel or a UIImageView, it would not show up in the UICollectionView Header). Preferably I would design the header in a xib, but since that's not working I tried to do it programmatically, but now labels are overlapping.
Collection views re-use cells and headers. When a header is scrolled offscreen it is added to a queue, and will be re-used for the next header to be scrolled onscreen. That means your configuration code in collectionView:viewForSupplementaryElementOfKind: method will be run again on the same header view at different indexPaths. Since you're just adding a label every time your configuration code runs, they will stack one on top of the other.
The correct approach is to have a label property on the headerView, so that its text can be changed. But...
no matter what I put in it (e.g., a UILabel or a UIImageView), it would not show up
This is your real issue. Do you have autolayout enabled on the xib? If so be sure to add constraints for the label, otherwise it will have zero width and height, giving the effect you describe.
Swift 3 version, hope it helps
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableview: UICollectionReusableView?
reusableview = nil
reusableview = playersCollectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "collViewHeader", for: indexPath as IndexPath)
if kind == UICollectionElementKindSectionHeader {
//remove all previous subviews:
if let views = reusableview?.subviews {
for view in views {
view.removeFromSuperview()
}
}
//----------
if reusableview == nil {
reusableview = UICollectionReusableView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 50))
}
//init label
let label = UILabel.init(frame: CGRect(x: 10.0, y: 20.0, width: self.view.frame.width-20, height: 25.0)) //-20 from width to make it no go out of screen bounds
label.textColor = UIColor.blue //you may set your color here :)
label.adjustsFontSizeToFitWidth = true
label.text = "your text here"
reusableview?.addSubview(label)
}
return reusableview!
}
ScoreboardReusableView is blank but it was reusable. You must put UILabel into header xib and set text for it after that.
For example:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
Scoreboard *thisScoreboard = [self.scoreboards objectAtIndex:indexPath.section];
ScoreboardReusableView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"
forIndexPath:indexPath];
view.label.text = [NSString stringWithFormat:#"%ld", (long)indexPath.section];
return view;
}
return nil;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
UICollectionReusableView *reusableview=nil;
reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
NSArray *viewsToRemove = [reusableview subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
if (reusableview==nil) {
reusableview=[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 30)];
}
reusableview.backgroundColor=[UIColor colorWithRed:234.0/255.0 green:234.0/255.0 blue:234.0/255.0 alpha:1.0];
UILabel *lblTitle=[[UILabel alloc] initWithFrame:CGRectMake(10, 0, [UIScreen mainScreen].bounds.size.width-90, 30)];
[lblTitle setFont:[UIFont fontWithName:#"Ubuntu-Light" size:12]];
lblTitle.textColor=HTAtHomeColor;
[reusableview addSubview:lblTitle];
UILabel *lblCount=[[UILabel alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width-80, 0, 70, 30)];
lblCount.textAlignment=NSTextAlignmentRight;
lblCount.textColor=[UIColor blackColor];
[lblCount setFont:[UIFont fontWithName:#"Ubuntu-Light" size:12]];
lblCount.text=[NSString stringWithFormat:#"%d Section Header",indexPath.row];
[reusableview addSubview:lblCount];
return reusableview;
}
return nil;
}

Display section header on top with UICollectionViewFlowLayout scrolling set to horizontal

This is a duplicate of this question. I'm asking again because the accepted answer is not working and no one's providing more explanation on how the supposed correct answer works.
So here's the situation: I want to display the collection view into one single row. To do this, I applied a custom UICollectionViewFlowLayout to the collection view and set the scrolling to horizontal. Everything is working fine, except for section header disappeared.
To remedy this, I implemented this function:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeMake(350, 35);
}
Now the header is shown, but the problem is it is displayed to the left of the cells, instead of the usual top.
I stumbled upon the link above while searching for a solution, but like I've said, the accepted answer is not working at all and I could not find other solutions about this situation. So can anyone help me here?
we can do that by using the delegate method -
(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
and keeping the left inset to minus value of width of supplementary view and managing the top inset
Have you tried using the header with something like this?
First: set it up in viewDidLoad...
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.headerReferenceSize = CGSizeMake(self.collectionView.bounds.size.width, 30);
// add any other setup you need
[self.collectionView setCollectionViewLayout:flowLayout];
Second: add header view ...
#define LABEL_TAG 128
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:
UICollectionElementKindSectionHeader withReuseIdentifier:#"SectionHeader" forIndexPath:indexPath];
UILabel *label = (UILabel *)[headerView viewWithTag:LABEL_TAG];
if (!label) {
label = [[UILabel alloc] initWithFrame:CGRectInset(headerView.bounds, 5, 5)];
label.tag = MY_HEADER_LABEL_TAG;
label.font = [UIFont boldSystemFontOfSize:12];
label.textColor = [UIColor redColor];
[headerView addSubview:label];
}
label.text = [NSString stringWithFormat:#"Section %d", indexPath.section];
return headerView;
}

Resources