Header views height for grouped UITableView on iOS7 - header

I'm building my settings screen and using a grouped table view.
When trying to set the headers I see spacing above my header view.
I double checked and I do pass the correct view height in -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section.
Here is a screenshot of this behavior:
You can see my view with the title (VIBRATE, SILENT MODE) in it and it's darker bg color and the brighter space above it.

After much searching, I have finally found a fix for this. The tableview's delegate needs to implement heightForFooterInSection and return a very small number. Returning 0 defaults to the same spacing that was causing the extra spaces.
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return CGFLOAT_MIN;
}

Try this:
- (void)viewWillAppear:(BOOL)animated{
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 1;
UIView *headerView = [[UIView alloc] initWithFrame:frame];
[self.tableView setTableHeaderView:headerView];
}

This is pretty much the same as Casey's response, however, it is a bit cleaner as it doesn't require implementing a delegate method. When you are setting up your table view, simply set the property sectionFooterHeight to 0. It accomplishes the same thing with less code (and no DBL_MIN oddness).
tableView.sectionFooterHeight = 0.0;

Pretty sure it is just a simple hack. But an easy way to do it is to write this function:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 48.0f; // header height
}
to customize its height.
Pretty sure there are other ways to do it, that I don't know of.

It seems that Apple made a conscious design decision to make grouped table views have extra space on top. Try adjusting the contentInset of the UITableView. See my answer here

Swift 2.2 version:
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.min
}

Related

How to make UICollectionheader transparent while scrolling up?

Please help me to make the UICollectionview header transparent while we scroll the collection view up. I have used UICollectionReusableView and set it background as clear color, but no use. WHILE MOVING UP THE HEADER ( SHOWN IN BLACK COLOR) SHOULD BECOME TRANSPARENT.... THAT IS THE ORANGE BACKGROUND SHOULD BE SLIGHTLY VISIBLE....
Not enough info, but I'll try to answer
First, you should keep pointer to your header view constantly
#property UICollectionReusableView *collectionHeaderView;
Create it once in viewDidLoad and than use it as a header view (whatever that means) in your collection view.
Next, to change collor on dragging up you'll need delegate method.
UICollectionViewDelegate conforms to UIScrollViewDelegate, so you can use its method
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
CGPoint translation = [scrollView.panGestureRecognizer translationInView:scrollView.superview];
if(translation.y > 0)
{
//dragging down
collectionHeaderView.backgroundColor = <#Whatever you need#>;
} else
{
// dragging up
collectionHeaderView.backgroundColor = [UIColor clearColor];
}
}
Special thanks to #mvielbut for answer
Update
According to comments to mvielbut's answer, this method is not always reliable, so you can use another approach (you'll need to check offset.y instead).

Resizing UICollectionViewCell After Data Is Loaded Using invalidateLayout

This question has been asked a few times but none of the answers are detailed enough for me to understand why/how things work. For reference the other SO questions are:
How to update size of cells in UICollectionView after cell data is set?
Resize UICollectionView cells after their data has been set
Where to determine the height of a dynamically sized UICollectionViewCell?
I'm using MVC but to keep things simple lets say that I have a ViewController that in ViewWillAppear calls a web service to load some data. When the data has been loaded it calls
[self.collectionView reloadData]
The self.collectionView contains 1 UICollectionViewCell (let's call it DetailsCollectionViewCell).
When self.collectionView is being created it first calls sizeForItemAtIndexPath and then cellForItemAtIndexPath. This causes a problem for me because it's only during cellForItemAtIndexPath that I set the result of the web service to DetailsCollectionViewCell via:
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"detailsCell" forIndexPath:indexPath];
((DetailsCollectionViewCell*)cell).details = result;
DetailsCollectionViewCell has a setter for the property details that does some work that I need to happen first to know what the correct cell size should be.
Based on the linked questions above it seems like the only way to fire sizeForItemAtIndexPath after cellForItemAtIndexPath is to call
[self.collectionView.collectionViewLayout invalidateLayout];
But this where the other questions don't work for me because although it calls sizeForItemAtIndexPath and allows me to grab enough information from DetailsCollectionViewCell to set the correct height it doesn't update the UI until after the user scrolls the UICollectionView and my guess is that it has something to do with this line from the documentation
The actual layout update occurs during the next view layout update cycle.
However, i'm stumped on how to get around this. It almost feels like I need to create a static method on DetailsCollectionViewCell that I can pass the web service result to during the first sizeForItemAtIndexPath pass and then just cache that result. But i'm hoping there is a simple solution to having the UI automatically update instead.
Thanks,
p.s. - First SO question so hope i followed all the rules correctly.
Actually, from what I found, calling to invalidateLayout will cause calling sizeForItemAtIndexPath for all cells when dequeuing next cell (this is for iOS < 8.0, since 8.0 it will recalculate layout in next view layout update).
So the solution i came up with, is subclassing UICollectionView, and overriding layoutSubviews with something like this:
- (void)layoutSubviews
{
if ( self.shouldInvalidateCollectionViewLayout ) {
[self.collectionViewLayout invalidateLayout];
self.shouldInvalidateCollectionViewLayout = NO;
} else {
[super layoutSubviews];
}
}
and then calling setNeedsLayout in cellForItemAtIndexPath and setting shouldInvalidateCollectionViewLayout to YES. This worked for me in iOS >= 7.0. I also implemented estimated items size this way. Thx.
Here my case and solution.
My collectionView is in a scrollView and I want my collectionView and her cells to resize as I'm scrolling my scrollView.
So in my UIScrollView delegate method : scrollViewDidScroll :
[super scrollViewDidScroll:scrollView];
if(scrollView.contentOffset.y>0){
CGRect lc_frame = picturesCollectionView.frame;
lc_frame.origin.y=scrollView.contentOffset.y/2;
picturesCollectionView.frame = lc_frame;
}
else{
CGRect lc_frame = picturesCollectionView.frame;
lc_frame.origin.y=scrollView.contentOffset.y;
lc_frame.size.height=(3*(contentScrollView.frame.size.width/4))-scrollView.contentOffset.y;
picturesCollectionView.frame = lc_frame;
picturesCollectionViewFlowLayout.itemSize = CGSizeMake(picturesCollectionView.frame.size.width, picturesCollectionView.frame.size.height);
[picturesCollectionViewFlowLayout invalidateLayout];
}
I had to re set the collectionViewFlowLayout cell size then invalidate his layout.
Hope it helps !

UITableVIew header without bouncing when pull down

I have a UITableView which I am able to add a header view to fairly easily. Many apps (like Facebook, for viewing events) have a headerView that when you pull down, the header view stays put but the rest of the table (the UITableViewCell's) are bouncing. When scrolling up the header disappears. How can I achieve this functionality?
Right now when I pull down the UITableView, even the headerView bounces as well
You can achieve this effect quite easily by adding a subview to the header view and adjusting its frame or transform when the table view is scrolled beyond the top, i.e. the y component of its contentOffset becomes negative.
Example (in a UITableViewController subclass):
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat headerHeight = 64.0f;
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, headerHeight)];
UIView *headerContentView = [[UIView alloc] initWithFrame:headerView.bounds];
headerContentView.backgroundColor = [UIColor greenColor];
headerContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[headerView addSubview:headerContentView];
self.tableView.tableHeaderView = headerView;
}
//Note: UITableView is a subclass of UIScrollView, so we
// can use UIScrollViewDelegate methods.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
UIView *headerContentView = self.tableView.tableHeaderView.subviews[0];
headerContentView.transform = CGAffineTransformMakeTranslation(0, MIN(offsetY, 0));
}
(to keep it simple, I've just used the first subview of the actual header view in scrollViewDidScroll:, you may want to use a property for that instead.)
Your UITableView is most likely working properly. Section headers are sticky by default in Plain style tables. Meaning as you scroll down the header stays at the top of the UITableView's frame until the next section header pushes it out of the way. The opposite occurs when you scroll up. Conversely you get the sticky behavior on section footers at the bottom of the UITableView's frame.
EDIT Misunderstood the original question:
I would suggest using a section header rather than the table view header to get the sticky behavior you're looking for.
Include a section in your data with no rows and put your table header's view in that section header view.
you can use this line in view did load: (swift 5.6)
tableView.bounces = false
There is 2 ways you can set the table header:
Using the .tableHeaderView property directly (this header scrolls with the table)
Overriding the - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section function (this header stays static with the section)
By the sounds of it you should use the 2nd method instead of using the .tableHeaderView property

Resize UICollectionView cells after their data has been set

My UICollectionView cells contain UILabels with multiline text. I don't know the height of the cells until the text has been set on the label.
-(CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
This was the initial method I looked at to size the cells. However, this is called BEFORE the cells are created out of the storyboard.
Is there a way to layout the collection and size the cells AFTER they have been rendered, and I know the actual size of the cell?
I think your are looking for the invalidateLayout method you can call on the .collectionViewLayout property of your UICollectionView. This method regenerates your layout, which in your case means also calling -collectionView: layout: sizeForItemAtIndexPath:, which is the right place to reflect your desired item size. Jirune points the right direction on how to calculate them.
An example for the usage of invalidateLayout can be found here. Also consult the UICollectionViewLayout documentation on that method:
Invalidates the current layout and triggers a layout update.
Discussion:
You can call this method at any time to update the layout information. This method invalidates the layout of the collection view itself and returns right away. Thus, you can call this method multiple times from the same block of code without triggering multiple layout updates. The actual layout update occurs during the next view layout update cycle.
Edit:
For storyboard collection view which contains auto layout constraints, you need to override viewDidLayoutSubviews method of UIViewController and call invalidateLayout collection view layout in this method.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[yourCollectionView.collectionViewLayout invalidateLayout];
}
subclass UICollectionViewCell and override layoutSubviews like this
hereby you will anchor cell leading and trailing edge to collectionView
override func layoutSubviews() {
super.layoutSubviews()
self.frame = CGRectMake(0, self.frame.origin.y, self.superview!.frame.size.width, self.frame.size.height)
}
Hey in the above delegate method itself, you can calculate the UILabel size using the below tricky way and return the UICollectionViewCell size based on that calculation.
// Calculate the expected size based on the font and
// linebreak mode of your label
CGSize maximumLabelSize = CGSizeMake(9999,9999);
CGSize expectedLabelSize =
[[self.dataSource objectAtIndex:indexPath.item]
sizeWithFont:[UIFont fontWithName:#"Arial" size:18.0f]
constrainedToSize:maximumLabelSize
lineBreakMode:NSLineBreakByWordWrapping];
- (void)viewDidLoad {
self.collectionView.prefetchingEnabled = NO;
}
In iOS 10, prefetchingEnabled is YES by default. When YES, the collection view requests cells in advance of when they will be displayed. It leads to crash in iOS 10

UITableView don't float section headers

Is it possible to not float the section headers for a UITableView with style UITableViewStylePlain?
I'm building AcaniChat, an open-source version of iPhone's native Messages app, and I want to make the timestamps section headers, but they shouldn't float.
I know that section headers don't float for table views of style UITableViewStyleGrouped, but that style looks less like what I'm going for. Should I just use that style and restyle the table view to make it look how I want?
I might do that if I can figure out how to make https://stackoverflow.com/questions/6564712/nsfetchedresultscontroller-nsdate-section-headers.
The interesting thing about UITableViewStyleGrouped is that the tableView adds the style to the cells and not to the TableView.
The style is added as backgroundView to the cells as a class called UIGroupTableViewCellBackground which handles drawing different background according to the position of the cell in the section.
So a very simple solution will be to use UITableViewStyleGrouped, set the backgroundColor of the table to clearColor, and simply replace the backgroundView of the cell in cellForRow:
cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];
I guess either you will have to use two kinds of custom tableCells or skip the tableview entirely and work on a plain scrollview to achieve this kind of style.
This can now be done in two quick and easy steps (iOS 6 only):
Change your UITableView style to UITableViewStyleGrouped. (You can do this from Storyboard/NIB, or via code.)
Next, set your tableview's background view to a empty view like so [in either a method such as viewDidAppear or even in the cellForRow method (though I would prefer the former)].
yourTableView.backgroundView = [[UIView alloc] initWithFrame:listTableView.bounds];
Voila, now you have your table view - but without the floating section headers. Your section headers now scroll along with the cells and your messy UI problems are solved!
This works because UITableViewStyleGrouped seems to now work by adding a background view to a plain UITableView, but without the floating section headers.
[N.B. Earlier to iOS 6, individual background images were added to UITableViewCell's.]
Do try this out and let me know how it goes.
Happy coding :)
EDIT: for iOS 7, simply change the table view style to 'UITableViewStyleGrouped' and change the view's tint color to 'clear color'.
You can achieve this by putting the headers into their own sections. First double your number of sections. Then for the even-numbered sections, return your header as the header and zero as the number of rows. For the odd-numbered sections, return nil for the header.
Assuming you're using an NSFetchedResultsController, it would look something like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.fetchedResultsController.sections.count * 2;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ((section % 2) == 0)
{
section /= 2;
id<NSFetchedResultsSectionInfo> sectionInfo = self.fetchedResults.sections[section];
return sectionInfo.name;
}
else
{
return nil;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ((section % 2) == 0)
{
return 0;
}
else
{
section /= 2;
id<NSFetchedResultsSectionInfo> sectionInfo = self.fetchedResults.sections[section];
return sectionInfo.numberOfObjects;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((indexPath.section % 2) == 0)
{
return nil;
}
else
{
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section/2];
id object = [self.fetchedResultsController objectAtIndexPath:indexPath];
// configure your cell here.
}
}