Swift determine if you have enough cells to cover the whole screen - ios

Is there an easy way do determine if you have enough TableViewCells to cover the whole screen? Like the whole screen space reserved for the TableView is covered in cells.
I know you can add up the height of each cell and the height of group headers and the StatusBar and any other elements in the screen and see if the sum if less than the height of the screen of the used device. But is there a more elegant and easier way to know?

You can count it by something like,
let tableViewLastPoint = self.yourTableView.tableFooterView!.frame.origin.y + self.yourTableView.tableFooterView!.frame.size.height
then compare that if tableViewLastPoint is greater than your screen's height then you have cell that requires more height then screen, else cells are not required full screen!
In this case you should initialize your tableview's footerview with zero frame like,
self.yourTableView.tableFooterView = UIView.init(frame: CGRectZero)

Related

Dynamic UILabel size for view before uitableview

I am having abit of trouble here trying to make this post description label to grow and shrink based on content size on IOS9. I have a view (I will refer it topView) that I am using as a header for the tableview (So when I scroll up the header disappears). Inside the topView, there are a bunch of stack views. I wish to grow and shrink the post description label in height based on content size. I do know how to do it in simple case where everything is inside the prototype cell (i.e. set estimated row height and set uitableviewautomaticDimensions, set sizetoFit on label and change number of lines to 0). However, this is a different case because the post description label is not really inside the cell, it is in its view before the table view cells.
Note that all items in the view has static height except the postdescription label. Post description label is inside a stack view that is pinned only left and right (So that top and bottom would grow?). Also, the main stack view that contains all elements is pinned towards the four sides with the topview that contains the main stack view also pinned towards the four sides. With this setup, I would expect the topview to grow and shrink based on the content size. However, I do not see that in the output. I dont know if it is the stackview that is holding the label refusing to grow or the top view refusing to grow to allow more space for the stackview for the label. Thanks
UPDATE
Thanks Riadluke, I tried doing something as suggested which is resizing the headerview after calculating the required height. I have placed the following code in viewDidLayoutSubview and it works with an issue
postDescriptionLbl.sizeToFit()
let headerView = commentTableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = TopStackView.frame.size.height + ImageStackView.frame.size.height + postDescriptionLbl.frame.size.height + SpacerStackView.frame.size.height + BottomStackView.frame.size.height
headerView.frame.size.height = height
commentTableView.tableHeaderView = headerView
The issue I now have with this method is that when the view controller appears, I can physically see the postDescription label height grow from the default height in storyboard to the required height. For example, when the VC first appears, I see a line of label with some string being cut off, however after 0.5 second, the headerview and the label grow to the size that I wanted. I know this would be expected because I was calling the manipulation after the viewDidLayout Subview. I was wondering if there is a better way such that I dont see that transition and the view appears to be the right height straight away. Ie. let the view know exactly how high the label is to determine how high the headerview needs to be before appearing on screen?
I'm afraid the view set as tableHeaderView of a UITableView does not get resized automatically. Its height will be fixed to the height it had in IB.
What you have to do is set its size manually and then reassign it as the tableHeaderView so it is displayed in the height you want.
It could take only few lines since you're using autolayout.
You can try this code right after you've set the header view's contents:
//for the target size you have set the width as your tableView's width when it is already displayed on screen.
//note that when it is accesed inside viewDidLoad the tableView's bounds
//may be different to the actual bounds it will be displayed with,
//here I am just using the screen bounds
let targetSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 10000)
//set the tableHeader's size to its size after its layout constraints are resolved
tableHeader.bounds.size = tableHeader.systemLayoutSizeFittingSize(targetSize)
//reassign it as the tableHeaderView to update the height it will be displayed in
tableView.tableHeaderView = tableHeader
After many many attempts, I could not get anything to work with the original setup. The best I achieved was to resize it after view did appear which is not idea as you see the previous layout.
It is now working with a complete different approach. I have created two prototype cell and have one as "HeaderViewCell" and implemented the following functions
commentTableView.estimatedSectionHeaderHeight = 400
commentTableView.sectionHeaderHeight = UITableViewAutomaticDimension
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
Everything works like a charm after that.

How to Add Margin Between UITableViewCells Without Being Hacky

A lot of the solutions I've seen here include changing the cell's background to an image and using sections for rows rather than just rows themselves. I'm looking to have only two sections and have each cell expand in height on tap, so neither of those solutions would work.
I saw one solution includes setting the frame of the cell in the layoutSubviews() function like so:
override func layoutSubviews() {
super.layoutSubviews()
self.frame = CGRectOffset(self.frame, 0, 10);
}
When I do this however, it only gives margin to one cell and that's only when I tap on the cell.
Is there a surefire way to add spacing in between UITableViewCells without being hacky and breaking the cell layouts in the process?
I did this yesterday pretty easily with auto layout.
I set the background of the cell and it's content view to clear, then I created a new view and setup constraints all around it and put my labels inside of it. The height changes dynamically based on the label so I needed to use UITableViewAutomaticDimension for the row height and give it an estimated row height as well.
I don't see why this wouldn't work for expanding it on a tap as well, you just might have to reload the cell.
make the cell and it's contentView transparent
contentView addSubview customContentView and layout your cell on customContentView
customContentView pin to contentView top leading trailing with offset 0 but pin to bottom with offset 10 //the margin height

UICollectionViewCell Snap to Centre

I have added a UICollectionView which will have 5 cells, scrolling horizontally. I would like the user to be able to scroll between the cells, where each cell will snap to the centre. Here is a my UICollectionFlowLayout code used with the cell sizes etc.
-(UICollectionViewFlowLayout*)collectionLayout{
if (!_collectionLayout) {
_collectionLayout = [[UICollectionViewFlowLayout alloc]init];
_collectionLayout.minimumInteritemSpacing = 0;
_collectionLayout.minimumLineSpacing = 30;
_collectionLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionLayout.itemSize = CGSizeMake(200, 165);
_collectionLayout.sectionInset = UIEdgeInsetsMake(0, 65, 0, 55);
_collectionLayout.collectionView.pagingEnabled = YES;
}
return _collectionLayout;
}
I have added insets so the first and last cell stops in the middle, though any of the three cells in between don't. Please see attached screen-shots to illustrate -[2 screen shots below]
I can easily achieve centralised paging of the cells if they are the width of the screen or I simply make 5 sections, however if I do this, then the user does not see the other cells left or right, which I need so they know there is more to scroll to, if you know what I mean.
I have read similar answers about disabling paging and using scrollViewWillEndDragging methods, but I could not get these to work either.
If anyone can offer any clear way to do this that would be great,
Thanks in advance
Jim
This likely won't be the solution you're ideally looking for, though it's an option you can use, as I have used for the set up you describe.
Increase your Uicollectionviewcell size to the width of the screen, with your purple square as view centralised within the cell. Remove your section inset code and change line spacing to =0.
At the left and right sides of the cell place arrow images (or as button), or a swipe gesture icon so app user knows there's more. As you've only got 5 cells, have cell at IndexPath.row==0 and IndexPath.row==4 to hide their left and right 'more' arrows respectively.

runtime expansion of uitableview footerview using autolayout

I have a UIView that is a footerview of a uitableview. At run time, the user enters text into a uitextview within the footerview that should adjust to the size of the text content with a height constraint in autolayout.
All other objects in the view (labels, imageviews) have appropriate constraints to accommodate the expansion of the textview.
HOWEVER the height of the overall footerview will not change size, and it is impossible to use autolayout on the tableview footerview height.
Does anyone have a solution? Thanks
Haven't found an actual, elegant, solution yet, but I've postponed fixing this by using a workaround:
Setting the frame of the view used as a footer to be as large as you might possible need. In my case this meant giving it about 60px of spare vertical room. Since it's the footer and there's nothing below it to reposition the user won't be affected by the workaround.
The contents of the footer view are pinned to the top and have enough space to expand when needed.
For the record: my view is loaded from a nib file.
Although in theory the size one gives to the top level view in interface builder is just for design-time and the runtime size should be calculated based on constraints and the resulting intrinsic size, for this specific case I found the height stays the same as it was in IB.
We can change the height of the footer view run time by the following code:
func methodToChangeTableViewFooterHeight()
{
var footerView:UIView = self._tableView.tableFooterView! as UIView
var frame:CGRect = footerView.frame
frame.size.height = self.heightCollectionCS.constant + 10
footerView.frame = frame
self._tableView.tableFooterView! = footerView
}
Here , self.heightCollectionCS.constant is the height constraint for our Collection View.
We can use text content height on that place.
You may try to set again the footer view each time you footer height changes, to inform the table it should change the footer height. Or use inset. From within the footer view:
SetNeedsLayout()
LayoutIfNeeded()
ownertable.TableFooterView = this
Sorry about that, misread that question long ago. You can access the footer directly through the tableview's property tableFooterView.
What you could do is create your default footer in a xib or in your viewDidLoad:. Once you need to increase the size of the footer, you can pull out the UIView from that property and edit its frame if necessary to make it larger.
So make sure the tableFooterView gets assigned a UIView because it is nil by default. To just make the height taller, you can use self.tableView.tableFooterView.frame = CGRectMake(whatever rect you need);

How to properly add custom TableViewCell?

I have a grouped tableView in my iPad-app, and I've been trying to set cell.imageView.center = cell.center to center the image instead of putting it to the leftmost position. This is apparently not possible without a subclass of the UITableviewCell(If someone could explain why, that'd also be appreciated.. For now I just assume they are 'private' variables as a Java-developer would call them).
So, I created a custom tableViewCell, but I only want to use this cell in ONE of the rows in this tableView. So in cellForRowAtIndexPath I basically write
cell = [[UITableViewCell alloc]initWith//blahblah
if(indexPath.row == 0)
cell = [[CustomCell alloc]initWith//blahblah
This is of course not exactly what I'm writing, but that's the idea of it.
Now, when I do this, it works, but the first cell in this GROUPED tableView turns out wider than the rest of them without me doing anything in the custom cell. The customCell class hasn't been altered yet. It still has rounded corners though, so it seems it knows it's a grouped tableView.
Also, I've been struggling with programmatically getting the size of a cell, in cellForRowAtIndexPath, I've tried logging out cell.frame.size.width and cell.contentView.frame.size.width, both of them returning 320, when I know they are a lot wider.. Like, all the rows are about 400 wide, and the first cell is 420 or something. It still writes out 320 for all the cells..
This code will not work for a couple of reasons:
cell.imageView.center = cell.center;
Firstly, the center is relative to its superview. I believe the cells superview is the tableView. The imageView's superview will be the content view of the cell. Therefore the coordinate systems are different so the centens will be offset. E.g. the 3rd cell down will have a center of 0.5 widths + 3.5 heights. You should be able to ge around this issue by doing:
cell.imageView.center = CGPointMake( width / 2 , height / 2 );
The second issue is related to how the table view works. The table view manages its cells view's. The width of a cell is defined by the table view's width and the height is defined by the table view's row height property. This means the cell itself has no control over its size.
You can however size its subviews, but you must do this after the cells size has been set (otherwise you can get strange results). You can do this in layout subviews (of the custom UITableViewCell class). See this answer.
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = ....
}
When layoutSubviews is called the cells frame has been set, so do your view logging here instead of cellForRowAtIndexpath.
As for the GROUPED style. Im not sure if this is designed to work with custom views. I suspect it sets the size of its cells to its own width minus a 20 pixel margin on each size, then applies a mask to the top and bottom cells in a section to get the rounded effect. If you are using custom view try to stick with a standard table view style.

Resources