I have a custom layout for uiCollectionView. Now if collectionView has 0 sections. And i push from collectionViewController to another controller and then pop. Now if section count is 0 collectionView will automatically invalidateLayout. How to stop this behaviour.
I have also override shouldInvalidateLayout but it never gets called.
Related
i have added a TableView inside Collection View Cell, but the Collection View Cell height should depend on TableView data.
Thanks.
Maybe this can be achieved by reloading the tableview first, waiting for it complete, fetch the tableview Content height then load the collectionview giving the cell the right values
tableView.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
requiredCellHeight = self.tableView.contentSize.height
})
This waits on the main thread for the tableview to load. Though not an ideal solution as we are blocking the main thread, might work for this complex setup
I've a tableView with custom cells.
Each cell has different interface, elements. All they have different sizes.
Each cell has different type of elements and their count.
All these elements are created dynamically, so I'm creating them, making their frames and adding as subviews.
So the problem that heightForRowAtIndexPath executes before cellForRowAtIndexPath where I'm creating the row and constructing its interface and I don't know how to pass calculated height to heightForRowAtIndexPath
How can I count row height before and pass its value to heightForRowIndexPath for correct drawing of my tableView?
If your cell view is really very complex and every component's height are depending on data source. You can try to create the view in heightForRowIndexPath method and then cache the created view to a dictionary in your view controller and use it directly in cellForRowAtIndexPath. In this way you only need to create the view once when user scrolling the table. If the datasource is not changing very frequently, you can reuse the cached view in heightForRowIndexPath as well.
And if the tableview has a lot of rows, you should return an approximate value for height in estimatedHeightForRowAtIndexPath to speed up the loading process of the view controller. Otherwise during loading tableview, it will try to calculate all row's height which may requires a lot of time.
But I really don't think your cell would be so complex. If only some UITextLabels that depends on datasource for the height, you can simply only calculate the height for the label, then add it to other components' height which is fixed.
You really should consider subclassing UITableViewCell and adding your custom logic inside your subclass.
Starting from there, you'll have such options:
Create static method in your cell that will receive all data necessary to draw your cell (e.g heithtForCellWithFirstString:secondString:accessoryImage etc) and calculate height using string size computation methods. Use this method inside heightForRowAtIndexPath.
Use autolayout for laying out subviews of your cell and then set table view's row height property to UITableViewAutomaticDimension. This way you won't need heightForRow delegate method at all. There are plenty of tutorials on this, for example: http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift
Try to create a subclass of UITableViewCell.
All elements created by you put inside UIView, which is put in a cell.
Create three NSLayoutConstraint, one from UIView.top to top of the cell, the second from UIView.bottom to the bottom of the cell and the third - the height of the UIView.
In the viewDidLoad method of tableViewController set row height as the UITableViewAutomaticDimension
:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
}
In a subclass of UITableViewCell create a method that will calculate the height of the cell and change NSLayoutConstraint, which sets the height of the cell. For example:
:
func adjustHeightOfInnerView() {
let height = 100
self.myInnerCellViewHeightConstraint.constant = height
self.contentView.setNeedsUpdateConstraints()
}
In the method cellForRowAtIndexPath call the adjustHeightOfTableview:
:
func tableView (tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! myTableViewCellSubclass
cell.adjustHeightOfTableview()
return cell
}
I have a custom cell with several labels hidden by default (the user sees all content when clicking on the cell). I'm changing the height of the cell and cell.clipToBounds = true to hide the labels. But this doesn't work for editable cells. When I start swiping the cell to left, the hidden content appears. I noticed that it works if I first expand the cell, collapse and the swipe, but stops working if I swipe another cell.
Any suggestions how to hide it when the cell is swiped?
Add the labels to an array.
Set hidden to true:
let labels = [firstLabel, secondLabel, etc.…]
for label in labels {
label.hidden = true
}
I think that is a bug with the setEditing overriding or ignoring clipsToBounds during the animation and edit.
The solution:
You should hide all the elements which should not appear when the table is "closed", and unhide them when the table is expanded.
1) Create a UITableViewCell method to hide/unhide all elements you need:
func hideAll(hide: Bool) {
myLabel.hidden = hide
myUISwitch.hidden = hide
}
2) Call the above method when you initialize your cells. Usually will be called somewhere inside the cell, on an initializer, or will be called on cellForRowAtIndexPath. I check for the cell.bounds.height to determine if the cell is expanded or not:
//cell init code
//I'm calling this method from inside the cell, so I use self here.
//If you're calling this method from your controller you should
//target the cell with cell.bounds.height, as well as targeting the
//cell to call the method: cell.hideAll(Bool)
var hidden = (self.bounds.height < 80) //change this number depending on your collapsed and expanded cell heights
hideAll(hidden)
3) At this point (if the above method was called inside cellForRowAtIndexPath or on a cell initializer) your method will be called when the cells are created and every time the cell expands. But not when the cell contracts (more on this ahead). So add some code in your editingStyleForRowAtIndexPath:
override func tableView(tableView: UITableView,
editingStyleForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCellEditingStyle{
if let cell = tableView.cellForRowAtIndexPath(indexPath) as? CustomTableViewCell {
cell.hideAll(cell.bounds.height < 80) //calls hideAll with a Bool argument, depending on height of cell
}
return .Delete
}
editingStyleForRowAtIndexPath is called only on the cell you swipe, and doesn't call any of the delegate methods such as heightForRowAtIndexPath, so I believe this is the best place to hide your view items.
Ok, that was the solution. Now the WHY:
The true problem when you want to hide these cells is when to hide them. First you have to understand the cycle that swift goes through with tableViewCells.
I'm assuming you're expanding/contracting the cells on the heightForRowAtIndexPath. Which semantically makes total sense. But let's suppose you have 3 cells (all expandable). I'm doing a println() whenever cellForRowAtIndexPath, didSelectRowAtIndexPath and heightForRowAtIndexPath are called. In the following example, I tapped on the first row, then on the second, then on the third. Observe the results on the console:
>>>> called didSelectRow at index 0
???? called heightForRow at index 0
???? called heightForRow at index 1
???? called heightForRow at index 2
???? called heightForRow at index 0
==== called cellForRow at index 0
???? called heightForRow at index 0
>>>> called didSelectRow at index 1
???? called heightForRow at index 0
???? called heightForRow at index 1
???? called heightForRow at index 2
???? called heightForRow at index 1
==== called cellForRow at index 1
???? called heightForRow at index 1
>>>> called didSelectRow at index 2
???? called heightForRow at index 0
???? called heightForRow at index 1
???? called heightForRow at index 2
???? called heightForRow at index 2
==== called cellForRow at index 2
???? called heightForRow at index 2
As you can see, when you didSelectRowAtIndexPath, heightForRowAtIndexPath is called multiple times, cyclically, until swift is sure it has all the heights right. Meanwhile, cellForRow is called only on the cell you selected.
So if your cells auto-collapse when you select another cell, you have to realize they're not running any code (such as the method to hide elements) when they collapse, because cellForRowAtIndexPath was called only on the tapped cell.
You could hide/unhide inside your heightForRow code, but that code is called so many times that it's really not optimal. Code should be called only once when you need it. So the best solution IMO is to call it inside the editingStyleForRowAtIndexPath
In your UITableViewCell subclass, override the willTransitionToState(state: UITableViewCellStateMask) method and set contentView.clipToBounds to true like this:
override func willTransitionToState(state: UITableViewCellStateMask) {
super.willTransitionToState(state)
contentView.clipsToBounds = true
}
From now on, it will clip the cell's subviews correctly.
try setting the contentView clipsToBounds property to true.
normally you shouldn't mess with the cell as a view.
Swift
cell.contentView.clipsToBounds = true
Obj-C
cell.contentView.clipsToBounds = YES
Things I want to do
Duplicate a CollectionView From ControllerA to ControllerB in storyboard
Things I have done
Duplicate(CMD+D) CollectionView From ControllerA(which works fine)
Drag Drop to ControllerB
retarget(ctrl + drag) dataSource and delegate to ControllerB
It should be
duplicated collectionView should work fine like it in ControllerA
But result is
numberOfSectionsInCollectionView and numberOfItemsInSection are called
But cellForItemAtIndexPath not called
Edit 1
the problem looks like related to the frame size of the CollectionView
CollectionView: size(320,50) flow layout,scroll horizontal
CollectionViewCell: size(50,50)
If I drag CollectionView height to 65,it works with a warning:(cellForItemAtIndexPath called)
the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView minus the section insets top and bottom values.
if CollectionView height >= 114,works without warning.
ControllerA with same parameters size(320,50) will work.don't know why
Edit 2
if I duplicate the CollectionView with all same settings and retarget to same delegate. the duplicate one doesn't work as expected. But the original one works!! weird!!
After upgrade to xcode 6 beta 5, I notice the cell is not in the visible area of CollectionView
Solution:
toggle off Adjust Scroll View Insets option in parent view controller attribute inspector
or add this code to parent view controller viewDidLoad method:
self.automaticallyAdjustsScrollViewInsets = NO;
ref:UICollectionView adds top margin
I'm looking to expand the height of the top cell in my collectionview with animation. The problem is when I detect a tap on the cell, I try animating the height and set the new height in the delegate: - collectionView:layout:sizeForItemAtIndexPath:indexPath. The issue I see an instant refresh after invalidating my collection flow layout and the animation happens afterwords. The effect I want is for the top cell to animate the expansion to the new height and push the rows beneath it along with the animation.
I'm only using one section with multiple rows. Should I try to use two sections and put the top cell in the first section and the remaining in the second section? Could I then animate the second section along with the height of the first section?
You need to do this three steps:
1.Invalidate your collectionViewLayout
self.collectionView.collectionViewLayout.invalidateLayout()
2.Reload desired indexPath or all the data inside a performBatchUpdates block
collectionView.performBatchUpdates({
self.collectionView.reload(at: DESIRED_INDEXPATH)
}, completion: nil)
3.Return the new height calculated in sizeForItemAtIndexpath delegate method