I am trying to create a collection view that has a header cell (dynamically sized) and some "normal" content cells (also dynamically sized). Therefore, the collection view is initialized as follows:
private lazy var timelineCollectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .vertical
flowLayout.sectionInset = UIEdgeInsets(top: self.coloredTitleBarHeight, left: 0, bottom: 0, right: 0) // assume that coloredTitlebarHeight is a constant of 120
flowLayout.estimatedItemSize = CGSize(width: self.view.frame.width, height: 10)
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
cv.alwaysBounceVertical = true
cv.contentInsetAdjustmentBehavior = .never
cv.backgroundColor = .clear
cv.scrollIndicatorInsets = UIEdgeInsets(top: self.coloredTitleBarHeight - self.getStatusBarHeight(), left: 0, bottom: 0, right: 0)
cv.delegate = self
cv.dataSource = self
cv.register(TLContentCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "content-header-cell")
cv.register(TLContentCell.self, forCellWithReuseIdentifier: "comment-cell")
return cv
}()
The collectionView is part of a ViewController that conforms to the following protocols: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
Then, I use the following code to create a header cell:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let cell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "content-header-cell", for: indexPath) as! TLContentCell
cell.timelineContent = tlContentItem
cell.delegate = self
return cell
}
Last but not least, the dequeuing of the regular cells:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "comment-cell", for: indexPath)
return cell
}
Output:
The collectionView displays the header cell as well as the item cells, however, the section Insets are not applied.
I would be very happy if you could provide me with any suggestion regarding this strange behavior.
Section insets are applied to contents only in UICollectionViewFlowLayout.
Using Section Insets to Tweak the Margins of Your Content
Section insets are a way to adjust the space available for laying out cells.
You can use insets to insert space after a section’s header view and
before its footer view. You can also use insets to insert space around
the sides of the content. Figure 3-5 demonstrates how insets affect
some content in a vertically scrolling flow layout. source
Related
I have a table view and collection view to display image. Table view for vertical scroll and collection view for horizontal scroll. The image is displayed as full screen for the device. But when I scroll vertically half of previous cell is displayed and half of next cell is displayed. I used Table view constant is 0 to superview on all 4 constraints.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return myTableView.frame.size.height;
}
To return the height of the cell but even this is causing same issue.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
return cell
}
This table view cell contains collection view
class TableViewCell: UITableViewCell,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
func setUpCollectionView(){
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: .zero,collectionViewLayout: layout)
collectionView?.register(VideoCollectionViewCell.self, forCellWithReuseIdentifier: VideoCollectionViewCell.identifier)
collectionView?.isPagingEnabled = true
collectionView?.dataSource = self
collectionView?.delegate = self
self.contentView.addSubview(collectionView!)
collectionView?.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView!.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView!.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
collectionView!.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView!.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.frame.width, height: self.frame.height)
}
}
This is my implementation, I don't want images to be displayed on half/half portion. I am trying to show it on full screen. Can anyone tell me why this is happening?
you need only UICollectionView display list images and set property scroll horizontal in UICollectionViewFlowLayout
myCollectionView.dataSource = self
myCollectionView.delegate = self
myCollectionView.contentMode = .scaleAspectFill
// scroll the images page by page, set property isPagingEnabled is true
myCollectionView.isPagingEnabled = true
let layout = UICollectionViewFlowLayout()
// set sroll horizontal here
layout.scrollDirection = .horizontal
myCollectionView.collectionViewLayout = layout
I am using a Collection View and I am able to add Section insets to the cells inside the collection view, but it appears not to be applying to the Collection Views reusable view, is there away to set section insets for reusable views?
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if(kind == UICollectionView.elementKindSectionFooter)
{
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ReviewsFooter", for: indexPath) as! ReviewFooterCell
footerView.reviewFooterDelegate = self
return footerView
}
else if(kind == UICollectionView.elementKindSectionHeader)
{
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ReviewHeader", for: indexPath) as! ReviewHeaderCell
headerView.totalReviews.text = "Total Reviews: " + String(self.reviews.count)
return headerView
}
fatalError()
}
First of all, let's be clear on what exactly is Section Insets.
Section insets are margins applied only to the items in the section.
They represent the distance between the header view and the first line
of items and between the last line of items and the footer view. They
also indicate the spacing on either side of a single line of items.
They do not affect the size of the headers or footers themselves.
Here is how you can add insets of a section in collectionView,
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
}
I have been working on an app that has a UICollectionView that works like the main screen with the UICollectionViewCells acting as different pages (scrolling horizontally). I have added a text field on each cell to edit, but when I click on the textfield, the cell height is extended when the keyboard appears. When the keyboard is hidden the cell height remains extended. I have been searching for an answer to this problem, but I have not come across a solution that works.
I have tried to invalidate the layout, set the translatesAutoresizingMaskIntoConstraints to false, and directly setting the content offset to zero. None of these options have fixed the issue.
Below is my code:
private let reuseIdentifier = ["Cell1", "Cell2", "Cell3", "Cell4", "Cell5"]
let navi_btn_array: [UIButton] = [navi_home_btn, navi_gavel_btn, navi_orders_btn, navi_profile_btn, navi_lightning_btn]
var collectionView: UICollectionView = {
var layout = UICollectionViewFlowLayout();
layout.sectionInset = UIEdgeInsets.zero
var cv = UICollectionView(frame: .zero, collectionViewLayout: layout);
cv.autoresizesSubviews = false
cv.contentInset = UIEdgeInsets.zero
cv.translatesAutoresizingMaskIntoConstraints = false;
return cv;
}();
class MainCVC: UICollectionViewController,UICollectionViewDelegateFlowLayout, CLLocationManagerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(SimpleDispensaryPage_Cell.self, forCellWithReuseIdentifier: reuseIdentifier[0])
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.isPagingEnabled = true
let nc:NotificationCenter = NotificationCenter.default
nc.addObserver(self, selector: #selector(keyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
nc.addObserver(self, selector: #selector(keyboardDidHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
#objc func keyboardDidShow(notification: Notification){
collectionViewLayout.invalidateLayout()
collectionView?.contentOffset.y = 0
}
#objc func keyboardDidHide(notification: Notification){
collectionViewLayout.invalidateLayout()
collectionView?.contentOffset.y = 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier[0], for: indexPath)
cell.backgroundColor = Custom_Colors.color_pine.withAlphaComponent(0.5)
var txt_fld = UITextField()
txt_fld.frame = CGRect(x: 0, y: 50, width: 100, height: 50)
cell_label.text = "Placeholder #"+String(indexPath.item)
cell_label.textAlignment = .center
cell.addSubview(cell_label)
return cell
}
}
}
I also get this error when it runs:
2019-03-14 11:41:20.770799-0700 app[57379:5390785] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
2019-03-14 11:41:20.771043-0700 app[57379:5390785] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fda15c13f40>, and it is attached to <UICollectionView: 0x7fda1706f000; frame = (0 0; 375 730.8); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000447bc0>; layer = <CALayer: 0x600000437720>; contentOffset: {8, -38.333333333333336}; contentSize: {1891, 579}; adjustedContentInset: {0, 0, 151.79999999999995, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7fda15c13f40>.
2019-03-14 11:41:20.771245-0700 app[57379:5390785] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
You have a little bit of strange setup here. I suggest making a couple of changes:
1 - Never add subviews in collectionView(_, cellForItemAt ...). This would result in multiple additions every time the cell is being reused. The subviews should be added by the cell (and preferably at creation time).
2 - Remove cv.autoresizesSubviews = false and cv.translatesAutoresizingMaskIntoConstraints = false.
3 - If you want a fixed size for your cells you can set the layout.delegate = self and implement the following method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: screenWidth, height: screenHeight)
}
4 - If you really just want a couple of pages why don't you use UIPageViewController?
my HomeHorizontalSpecialCell inherited UICollectionViewCell, and has a property which is collectionView. And this collectionView constructs by lazy loading
private lazy var _collectionView: UICollectionView! = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 10
let itemWidth = (kScreenWidth - 4 * layout.minimumLineSpacing) / 3.4
let itemHeight = itemWidth
layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: kScreenWidth, height: itemHeight), collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.white
collectionView.contentInset = UIEdgeInsetsMake(0, 7, 0, 7)
// register cell
collectionView.register(HomeHorizontalSpecialGoodCell.self, forCellWithReuseIdentifier: HomeHorizontalSpecialCell.reuseSpecialCellID)
collectionView.showsHorizontalScrollIndicator = false
self.contentView.addSubview(collectionView)
return collectionView
}()
when set data, this collectionView calls to reloadData method and begin to construct and register cell.
in dataSource methods, I dequeue these registered reusable cells
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// crash at here on big size iPhone
let specialGoodCell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeHorizontalSpecialCell.reuseSpecialCellID, for: indexPath) as! HomeHorizontalSpecialGoodCell
if indexPath.row != (_dataList?._item.count)! { // good cells
specialGoodCell.goodModel = _dataList?._item[indexPath.row]
} else { // last cell
specialGoodCell.setMoreImage("spe_more")
}
return specialGoodCell
}
force to unwrap a cell cause App crash when collectionView dequeue a cell on some big size iPhones, but small size not.
i found the situation where directly initial HomeHorizontalSpecialCell at first time at big size iPhone, but at small size it need to scroll collectionView to initial this cell instance.
why does app crash when loading this cell at big size iPhone?
someone can help?
A good way to do this is using the viewController as the datasource and delegate of the collectionView. Maybe this article can help you.
Putting a UICollectionView in a UITableViewCell in Swift
I have searched a lot for creating a UICollectionView programatically but none of them suggest the simplest way to use it, how to add a label or an image to the UICollectionViewCell. Most of the sites suggest that implementation of UICollectionView is same as UITableView, but the major difference comes when we try to add any image. In UITableView we can allocate the imageViews in cellForRow method where cell == nil and assign images where (cell != nil). but here in case of UICollectionView ItemAtIndexPath method, there is no condition (cell == nil) as in UITableView's CellForRow. As a result we can't effectively allocate variables of UImageViews or Labels etc in itemAtIndexPath method. I Want to know whether there is any alternative other than subclassing the UICollectionViewCell and allocating variables in that custom Class? Can any one help, any help is appreciated.
There is not alternative to create or allocate cells in itemAtIndex method. We need to register the customised class to create any views inside the custom class. something like this :
[UICollectionView registerClass:[CustomCollectionViewClass class] forCellWithReuseIdentifier:#"cellIdentifier"];
here is the best link which I found useful. Hope it helps others
swift :
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 70, height: 70)
let demoCollectionView:UICollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
demoCollectionView.dataSource = self
demoCollectionView.delegate = self
demoCollectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
demoCollectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(demoCollectionView)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 27
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
cell.backgroundColor = UIColor.lightGrayColor()
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
print("User tapped on item \(indexPath.row)")
}