How to make right scrollable menu with buttons? - ios

I want to make scrollable menu (first view controller) with 4 buttons. Every button linked with specific view controller. But I want to make it specific: after app launching user must see only first three buttons on every iPhone version (4, 4.7, 5.5) and he needs to scroll down to see one more button. Should I use stack view with scrollview? Should I use UICollectionView (I found that's good for images but not for buttons)?
Thanks everybody for help!

If you use a UICollectionViewController (or UITableViewController, either would work) you can layout your storyboard like this:
Then in your MyTableViewController (again, this also works with a UICollectionViewController) you can implement the following method to set the cell size equal to a third of the window height:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// get the height for the current view
let viewHeight = self.view.frame.height
// since we want 3 rows to appear at a time, simply divide the viewHeight by 3
return viewHeight / 3
}

Related

Auto Cell Height on UITableView without overlapping bottom part of iPhone X

So my goal is to achieve automatic height for my table view. What I mean by by that is whenever I add new item to the table view, the table is still "full screen" while the cell is decreasing its height.
for example this is what I've done :
on iPhone 6s Simulator
on iPhone 5s Simulator
and I've achieve that using code :
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let itemCount:CGFloat = CGFloat(items.count)
let statusHeight = UIApplication.shared.statusBarFrame.height
let navHeight = self.navigationController!.navigationBar.frame.height-statusHeight
let viewHeight = view.bounds.height
let cellHeight = (viewHeight-navHeight)/itemCount
return cellHeight
}
but the problem is that in iPhone X it was doing something like :
on iPhone X Simulator
the bottom part of the bottom item is cut/overlapped. How can I fix it?
Thank you.
Edit :
Thanks for the answer Trupesh Vaghasiya and Pankaj I've done your solution, couldn't find it but this is what I've done {Screenshot}
But the outcome was like this {iPhone X Screenshot}. Don't you think that's violates the iPhone X UI guideline?
You need to change TableView Bottom Constraint First Item Safe Area to give SupperView.
Let us know... is working?
Just change your Interface constraint relevant to superView instead of safe area. Check your tableview top and bottom Constraint and make it related to superView. but as per apple said, it is good if you make it related to safe area as iPhone X and higher version have home button on screen so. you can check bellow screen shots. Select Bottom Constraint
Select constraint that you want to change and Double Click at there.
And then select First Item and then select Superview

Insert in a tableView set to Autoresize cause a scroll issue

I've an issue for few days and really I can't explain why it goes like that.
I'm doing a chat, set up with a tableView, printing message into cell. These cells are designed with prototypes, there are 3 different type (but anyway it doesn't matter). Text is typed, message is send, cell is inserted in table and then we scroll to the bottom of the tableView.
As you know, in a chat view the container of the message has to fit this text (which is a view), and then the cell has to fit to this container (Label in orange, container in purple).
This container has a variable height and grow along the text, changing cell height.
I've set many constraint for auto-layout display but I didn't have different cell height than the height I initially set for it in the project (no adaptative behaviour). Chat Message were cut.
So, I've tried to set each rowHeight by myself using the method heightForRowAtIndexPath, calculating constraint along text size but it create a real bad behaviour (changing cells when scrolling for example). The size was often wrong calculated/recalculated.
That's why I'm finally using estimatedRowHeight set to 44 combine with UITableViewAutomaticDimension. Here it does the trick ! Waouh ! But it's not as good as expected..
When the table view appears all is good. Containers fit to their label, rows' height fit to their container and it's beautiful. The problem appears during an insert.
After many tests, I notice that this bad behaviour only appears when multilines label remains in the table view.
During an insert, each cell seems to resize to 44 before adapt its height to content, and then create a gap compared to previous state which create a strange scroll of the table view :
If I change the estimatedRowHeight it made this worst.
I can show my code if you want, but I don't think it will be very useful here because I'm not using complicated function.. only insert, automatic height for cells and scroll down which are functions delegate for tableView.
Can you help me please ? Really I don't know how to change that.. every chat application does the trick but I can't found out the way.
Thank you for answer, excuse my english level I'm a poor french student..
If you need some code comment I'll give it.
I think you can cache height value for every cell in cellForRowAtIndex method and in HeightForRowAtIndex set the corresponding height for that cell.
You can just do this way :
var cellCached = Dictionary<Int,AnyObject>()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
table.rowHeight = yourCell.yourLabel.frame.origin.y + yourCell.yourLabel.frame.height + 10
cellCached[indexPath.row] = yourTable.rowHeight
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(tableView == yourTable)
{
if(cellCached[indexPath.row] != nil)
{
return cellCached[indexPath.row] as! CGFloat
}
else
{
return 200
}
}
else
{
return UITableViewAutomaticDimension
}
}

How to adjust UICollectionView contentSize.height based on number of rows in swift

I'd really appreciate if someone can light up some ideas here. I've been trying to fix this for weeks now (not kidding).
I have a "to do list" using a collectionView where I hide the rows that were completed and move them to the end of the list. I then unhide the items if needed with a button. The collectionView looks exactly as a tableView with one item(cell) per row.
When the items are hidden the collectionView has a lot of empty scrolling space at the bottom instead of automatically deleting the space used by the hidden rows since they technically are still there.
I'm trying to cut that empty space so the collectionView height would be equal to the amount of cells/rows left visible.
override func viewDidAppear(animated: Bool) {
if isCellHidden { //checking if cells are hidden
var heightOfSection0 = 95 + (42 * self.collectionView.numberOfItemsInSection(0))
println(self.collectionView.contentSize.heigh) //returns 2350
self.collectionView.contentSize.height = CGFloat(heightOfSection0)
println(heightOfSection0) //returns 1019
println(self.collectionView.contentSize.heigh) //returns 1019.0 which is correct but as soon as I scroll down it resets to it's original size (2350) and let's me scroll through that empty space...
}}
If I try to read the collectionView height immediately after setting this, it displays the correct value but as soon as I try to scroll down it resets back to it's original height. I also tried disabling the auto layout and it doesn't make a difference
You should not manage contentSize directly - return appropriate number of items to be displayed from collectionView(_:numberOfItemsInSection:) instead (i.e. do not count your "hidden" cells).
You can use sizeForItemAtIndexPath: to change the size of collection view cell.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var numberOfCellInRow : Int = 3
var padding : Int = 5
var collectionCellWidth : CGFloat = (self.view.frame.size.width/CGFloat(numberOfCellInRow)) - CGFloat(padding)
return CGSize(width: collectionCellWidth , height: collectionCellWidth)
}
Changing UI stuff in viewDidAppear() in theory is a good place to do so. However, at this point, you can't be sure that auto layout has operated on the subview that you're manipulating.
You can check when it has and when it's safe to manipulate the frame, by subclassing it and overriding layoutSubviews()
I had a similar issue here: https://stackoverflow.com/a/30446125/4396258

UICollectionViewController Push Transitions

I'm trying to implement an interface where the user drills down through successive collection views of data, and would like to have each collection view animate to the next one as it's pushed. This has been very frustrating.
I've boiled it down to a very simple test case which I still can't get to work the way I think it ought to.
1- A UINavigationController has a UICollectionViewController which is hardwired to return a numberOfSections == 1, an itemsInSection = 8, and the backgroundColor of the cells is set to redColor in cellForItemAtIndexPath. The associated UICollectionViewFlowLayout.itemSize is set to 80, 80.
2 - When a cell is selected I push a new UICollectionViewController which is hardwired to return a numberOfSections == 1, an itemsInSection == 30, and the backgroundColor of the cells is set to blueColor in cellForItemAtIndexPath. The associated UICollectionViewFlowLayout.itemSize is set to 60, 60.
If I push the second controller with its useLayoutToLayoutNavigationTransitions property set to false, the controller slides in with the proper data display (30 blue cells). If I set useLayoutToLayoutNavigationTransitions to true before pushing, only the layout changes on push. The 8 red cells from the first controller are still displayed, but their size changes to 60, 60.
Apple's UICollectionViewController class reference says that when you set useLayoutToLayoutNavigationTransitions to true, "the navigation controller performs an animated layout change between the contents of the two collection view controllers instead of the traditional push animation", but I'm only getting the layout change.
My test code is stupid simple, so not sure what I might be doing wrong here. Does Apple's class reference misrepresent what the expected behavior should be? Is there any reason to just change layouts when you've pushed a new controller (and theoretically a new data source)? Is there an alternative way to animate to the next collection view?
For what it's worth, here is the only code in my test program other than returning appropriate values for numberOfSections and numberOfItems, and setting appropriate cell background colors.
override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let itemSize = (collectionViewLayout as! UICollectionViewFlowLayout).itemSize;
let theLayout = UICollectionViewFlowLayout()
theLayout.itemSize = CGSizeMake(itemSize.width - 20, itemSize.height - 20)
let toVC = SecondCollectionViewController(collectionViewLayout:theLayout)
toVC.useLayoutToLayoutNavigationTransitions = true
navigationController?.pushViewController(toVC, animated:true)
}

Is there a way to make estimated row heights with UITableView actually work with accurate scroll position?

Estimated row heights with UITableView are great for performance, but boy are they a headache when trying to have an accurate scroll offset with your table view.
This article from Ray Wenderlich details some of the issues at the bottom. As does this article. When you go to a new view controller and then return back to the table view after, the table view gets completely confused where you are and doesn't return the user to their previous location. The same can occur with rotation sometimes and even simple scrolling.
I can't help but wonder... what is the point then? Should it not bring you back to exactly where you were before?
I know that's difficult with estimates, but is there no way to accomplish that other than giving perfect estimates (which then aren't really estimates and defeat the purpose)? Because right now unless there's a single view controller with cells in it, any other situation, such as segueing to a new view controller seems to really mess it up.
How do I use estimates in a way that is actually useful and doesn't cause jumping all over the place?
I had exactly the same problem as you - a UITableView with different row heights embedded within a UIPageViewController. The TableView would scroll fine, I would swipe away and back onto the table, and the position would be all over the place.
Using both estimatedHeightForRowAtIndexPath and heightForRowAtIndexPath is what did the trick for me, as indicated in this article. From then on, whenever I swiped away and back onto the table, the position would remain the same. Hurray!
There was still a problem though: the first time the table is loaded estimatedRowHeights only gets the last half of the cells (rows 16 to 31 out of 32 cells for example) and therefore makes you start in the middle. As soon as I would scroll, the first half of the cells (rows 0 to 15) would load and I would be back on row 0 instead of row 15. After that the table would behave fine.
Although a minor problem, the work-around is to simulate a scroll up once the very last cell has been loaded. This triggers the loading of the first cells and brings your table to the "proper" row 0.
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// return whatever the cell height should be
return myCellsArray[indexPath.row]["height"] as CGFloat
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// return whatever the cell height should be
return myCellsArray[indexPath.row]["height"] as CGFloat
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Check that the final cell has been loaded and that this is the first time the table is loaded
if indexPath.row == myCellsArray.count - 1 && myStruct.tableHasLoaded == false{
// Reached the bottom
println("bottom reached with cell \(indexPath.row)")
// Simulate a mini scroll backwards as estimatedRowHeight only starts halfway and loads the final half, not the first half of the cells
// This way it forces it to go back to row 0
self.setContentOffset(CGPoint(x: 0, y: -1), animated: false)
// Set bool to true - we don't want to go back to the top everytime we reach the bottom of the table
myStruct.tableHasLoaded = true
}
}

Resources