I have a collectionView with images from a service request that are showed in lines with 3 elements.
What I need is that when a Have just one or two images from the request, the images shows aligned to the left, but the screen shows the images centered.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if imageFuelArray.count == 1{
let padding: CGFloat = 10
let collectionViewWidth = collectionView.frame.size.width - padding
let collectionViewHeight = collectionView.frame.size.height - padding
return CGSize(width: collectionViewWidth, height: collectionViewHeight)
} else{
let padding: CGFloat = 10
let collectionViewWidth = collectionView.frame.size.width - padding
let collectionViewHeight = collectionView.frame.size.height - padding
return CGSize(width: collectionViewWidth/2, height: collectionViewHeight/2)
}
}
I have this function
func getSizeVews(){
if imageFuelArray.count > 6{
self.collectionView.heightAnchor.constraint(equalToConstant: self.collectionView.contentSize.height+45).isActive = true
self.servicesTableView.heightAnchor.constraint(equalToConstant: self.servicesTableView.contentSize.height+70).isActive = true
view.layoutSubviews()
} else {
self.collectionView.heightAnchor.constraint(equalToConstant: self.collectionView.contentSize.height).isActive = true
self.servicesTableView.heightAnchor.constraint(equalToConstant: self.servicesTableView.contentSize.height+70).isActive = true
view.layoutSubviews()
}
}
And there I have my collectionView datasource and delegate
extension ServicesStationViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
imageFuelArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: flCollectionVieCell, for: indexPath) as! FuelCollectionViewCell
let fuel = imageFuelArray[indexPath.row]
if let cepsaFuel = CepsaFeaturesFuel(key: Int(fuel) ?? 0) {
let imgName = CepsaFeaturesFuel.featureFuelImageName(with: cepsaFuel.featuresFuelType)
cell.fuelImageView?.image = UIImage(named: imgName!)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if imageFuelArray.count == 1{
let padding: CGFloat = 10
let collectionViewWidth = collectionView.frame.size.width - padding
let collectionViewHeight = collectionView.frame.size.height - padding
return CGSize(width: collectionViewWidth, height: collectionViewHeight)
} else{
let padding: CGFloat = 10
let collectionViewWidth = collectionView.frame.size.width - padding
let collectionViewHeight = collectionView.frame.size.height - padding
return CGSize(width: collectionViewWidth/2, height: collectionViewHeight/2)
}
}
}
Related
I have taken collectionview in storyboard and in its cell i have taken ImageView and its below one label
i need to show only three rounded images horzontally and number of rows from JSON vertically in all screen sizes
like below image i need:
in cell for imageview costraints like below
leading = trailing = top = bottom = 5
and in coding:
class FriendsViewController: UIViewController {
// MARK: - IBOutlets
#IBOutlet weak var galleryCollectionView: UICollectionView!
// MARK: - LifeCycleMethods
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
layout.scrollDirection = .vertical
let width = UIScreen.main.bounds.width/4
let height = width*1.1
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 5
layout.itemSize = CGSize(width: width, height: height)
self.galleryCollectionView.collectionViewLayout = layout
}
}
class FriendsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgView: UIImageViewX!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
imgView.layer.cornerRadius = imgView.frame.height/2.0 }
}
so i am getting weird o/p in different screen sizes why?
o/p of ipodtouch7
In your collectionview cell Set constraints as shown in attached image, i.e. ur image view width should depend on imageview height, you can do it using ratio.
Then in willDisplay method calculate cornerRadius
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
guard let cell = cell as? TestCollectionViewCell else
{
return
}
let cellHeight : CGFloat = cell.frame.size.height
let labelHeight : CGFloat = cell.label?.frame.size.height ?? 0.0
cell.imageView?.layer.cornerRadius = (cellHeight - labelHeight) / 2.0
}
Below is the entire code
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet weak var collectionView : UICollectionView? = nil
let horizontalSpaceBetweenCell : CGFloat = 16
let verticalSpaceBetweenCell : CGFloat = 16
let edgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
override func viewDidLoad()
{
super.viewDidLoad()
setup()
}
func setup()
{
let nib = UINib(nibName: "TestCollectionViewCell", bundle: nil)
collectionView?.register(nib, forCellWithReuseIdentifier: "TestCollectionViewCell")
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 40
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionViewCell", for: indexPath) as? TestCollectionViewCell
{
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
guard let cell = cell as? TestCollectionViewCell else
{
return
}
let cellHeight : CGFloat = cell.frame.size.height
let labelHeight : CGFloat = cell.label?.frame.size.height ?? 0.0
cell.imageView?.layer.cornerRadius = (cellHeight - labelHeight) / 2.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let numberOfCellsPerRow = 3
let otherSpace = (edgeInsets.left + edgeInsets.right)
let width = (collectionView.frame.size.width - otherSpace) / CGFloat(numberOfCellsPerRow)
return CGSize(width: width, height: 80) // Height can be anything 80, 90 ,100
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return edgeInsets
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
}
}
Please change Widhth to height :
imgView.layer.cornerRadius = imgView.frame.size.height/2.0
I have a vertical scrolling Collection view and I want to make the centered cell bigger when scrolling happens.
I'm using InfiniteLayout for infinite scrolling on my collection view.
here is my custom flow layout:
import Foundation
import InfiniteLayout
class CustomLayout: InfiniteLayout {
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect).flatMap {
self.copyLayoutAttributes(from: $0)
}
guard let visibleRect = self.visibleCollectionViewRect() else {
return attributes
}
let centeredOffset = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
for attributes in attributes ?? [] {
let diff = self.scrollDirection == .horizontal ? centeredOffset.x - attributes.center.x : centeredOffset.y - attributes.center.y
let distance = abs(diff)
let tolerance : CGFloat = 0.02
var scale = 1.00 + tolerance - (( distance / centeredOffset.y ) * 0.105)
if(scale > 1.0){
scale = 1.0
}
if(scale < 0.460091){
scale = 0.460091
}
attributes.transform = attributes.transform.scaledBy(x: scale, y: scale)
attributes.alpha = changeSizeScaleToAlphaScale(scale)
}
return attributes
}
func changeSizeScaleToAlphaScale(_ x : CGFloat) -> CGFloat {
let minScale : CGFloat = 0.46
let maxScale : CGFloat = 1.0
let minAlpha : CGFloat = 0.25
let maxAlpha : CGFloat = 1.0
return ((maxAlpha - minAlpha) * (x - minScale)) / (maxScale - minScale) + minAlpha
}
}
but it doesn't work...
here is my delegate and data source:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return fakeData.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = infinteCollectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! MyCollectionViewCell
cell.setText(text: fakeData[infinteCollectionView.indexPath(from: indexPath).row])
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 55)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.height
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.infinteCollectionView.scrollToNearestVisibleCollectionViewCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
self.infinteCollectionView.scrollToNearestVisibleCollectionViewCell()
}
}
here is my extension for scroll to visible cell:
extension InfiniteCollectionView {
func scrollToNearestVisibleCollectionViewCell() {
self.decelerationRate = UIScrollView.DecelerationRate.normal
let visibleCenterPositionOfScrollView = Float(self.contentOffset.y + (self.bounds.size.height / 2))
var closestCellIndex = -1
var closestDistance: Float = .greatestFiniteMagnitude
for i in 0..<self.visibleCells.count {
let cell = self.visibleCells[i]
let cellHeight = cell.bounds.size.width
let cellCenter = Float(cell.frame.origin.y + cellHeight / 2)
// Now calculate closest cell
let distance: Float = fabsf(visibleCenterPositionOfScrollView - cellCenter)
if distance < closestDistance {
closestDistance = distance
closestCellIndex = self.indexPath(for: cell)!.row
}
}
if closestCellIndex != -1 {
self.scrollToItem(at: IndexPath(row: closestCellIndex, section: 0), at: .centeredVertically, animated: true)
}
}
}
Basicly I want to approach this:
Here is how I did it not sure if it's the best way to do it
Make a var to track the index in the middle of collection view
var index = IndexPath()
in the viewDidLayoutSubviews check for the cell in the middle once it's calculated invalidate the already configured layout
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let center = self.view.convert(collectionView.center, to: collectionView)
index = collectionView.indexPathForItem(at: center) ?? IndexPath(item: 0, section: 0)
collectionView.collectionViewLayout.invalidateLayout()
}
then in sizeforitem
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if index.count != 0 {
if index.item == indexPath.item {
return CGSize(width: collectionView.frame.size.width, height: 100)
}
}
return CGSize(width: collectionView.frame.size.width, height: 50)
}
then in scrollviewdidscroll call viewDidLayoutSubviews again so the centered element gets recalculated and item size gets changed
func scrollViewDidScroll(_ scrollView: UIScrollView) {
viewDidLayoutSubviews()
}
I have a 7 cells UICollectionView displayed like this
X X
X X
X X
X
I need it to be displayed like this
X X
X X
X X
X
How's this can be done in Swift?
you can do this simple calculation in your collectionViewLayout function.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
//return itemSize
let spaceBetweenCell :CGFloat = 0
let screenWidth = UIScreen.main.bounds.size.width - CGFloat(2 * spaceBetweenCell)
let totalSpace = spaceBetweenCell * 1.0
if indexPath.row == categories.count-1 {
// check if last cell is odd
if categories.count % 2 == 1 {
return CGSize(width: screenWidth , height: (screenWidth-totalSpace)/4) // all Width and same previous height
}else {
return itemSize
}
}else{
return itemSize
}
}
Here is my code just i add datasource count to 7 items
Also make sure that you create your cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:
"cell", for: indexPath) as! UICollectionViewCell
, Spacing between items =4
Code
extension ViewController :UICollectionViewDelegate , UICollectionViewDelegateFlowLayout,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7 // data source
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
cell.backgroundColor = .red
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let spaceBetweenCell :CGFloat = 4.0
let screenWidth = UIScreen.main.bounds.size.width - CGFloat(2 * spaceBetweenCell)
let totalSpace = spaceBetweenCell * 1.0; // there is one space in 2 item
if indexPath.row == 6 { // indexpath.row == datasource.count - 1
if 6 % 2 == 1{ // check if last cell is odd
return CGSize(width: screenWidth , height: (screenWidth-totalSpace)/2) // all Width and same previous height
}else{
return CGSize(width: (screenWidth - totalSpace)/2, height: (screenWidth-totalSpace)/2)
}
}else{
return CGSize(width: (screenWidth - totalSpace)/2, height: (screenWidth-totalSpace)/2)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4.0
}
}
I have two UICollectionView collectionViewA and collectionViewB in my ViewController, collectionViewB is presented as a subView when a button is tapped, the issue i have now is that when i scroll on collectionViewB, collectionViewA scrolls too, is there a way to only scroll for the active collectionView without affecting the second.?
extension TrendListVC: UICollectionViewDelegate,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.collectionViewForSubView{
return count
}
return 60
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionViewForSubView{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ModalViewCell
//let setting = settings[indexPath.item]
//cell.setting = setting
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath) as! CollectionCell
cell.itemNameLabel.text = "Name".uppercased()
return cell
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableView : CollectionHeader? = nil
if collectionView == self.collectionView{
if (kind == UICollectionElementKindSectionHeader) {
let head = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerCell", for: indexPath) as! CollectionHeader
head.trendListVc = self
head.headerHeightConstraint = headerHeightConstraint
reusableView = head
}
}
return reusableView!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.collectionViewForSubView{
if count % 2 != 0 && indexPath.item == count - 1{
let paddingSpace = sectionInsets.left * (itemsPerRowForSubView + 1)
let availableWidth = collectionViewForSubView.frame.width - paddingSpace
return CGSize(width: availableWidth + 23, height: cellHeight)
}else{
let paddingSpace = sectionInsets.left * (itemsPerRowForSubView + 1)
let availableWidth = collectionViewForSubView.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRowForSubView
return CGSize(width: widthPerItem, height: cellHeight)
}
}else{
let paddingSpace = sectionInsets.left * (2 + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem + 4, height: widthPerItem)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if collectionView == self.collectionView{
return CGSize(width: view.frame.width, height: 50)
}else{
return CGSize(width: 0, height: 0)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.collectionView{
showControllerForSetting(setting: "Name")
}else if collectionView == self.collectionViewForSubView{
print("Some thing")
}
}
}
This is most likely due to using your ViewController as delegate and datasource for both of your collection views. You need to create separate delegate classes for each collection view.
I am newbie in IOS dev . I was given job to make calendar app. I am using UICollectionViewCell to display days 1-30. for daynames like "S M T W TH F S" I used supplementary view(headerCell) and adding 7 UILabels with SPACING same as distance between two cells. I see Daynames are not aligned vertically to Day Numbers. I have extended UICollectionViewDelegateFlowLayout to set minimumInteritemSpacingForSectionAtIndex to 2 and I managed the cell sizes to display only 7 days(1 week) per row.
I also calculated distance between 2 cells using their centres , taking that distance as reference and adding it as distance between labels in HeaderCell, but still they are not getting aligned.
I am looking to exactly calculate distance between cells in UICollectionView. the spacing we set is just minimum and it differs sometimes depending on iPhone screen size.
Here is the Pic:
missing alignment with day cells
Here is my code:
extension FirstViewController: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var cwidth:CGFloat = 0.0
cwidth = (collectionView.frame.size.width/7 - PADDING - SPACING)
let cheight = collectionView.frame.size.height/7
cellWidth = cwidth
cellHeight = cheight
print("Cell width is \(cellWidth) and view width is \(collectionView.frame.size.width)")
return CGSize(width: cwidth, height: cheight )
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top:1.0,left:1.0,bottom:1.0,right:1.0)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 1.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return SPACING
}
}
class FirstViewController:
UIViewController,UICollectionViewDataSource,UICollectionViewDelegate
{
func collectionView(collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath:
NSIndexPath) -> UICollectionReusableView
{
let cell = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "headerCell", forIndexPath: indexPath)
var prevWidth:CGFloat = 5.0
var frame1 :CGRect? = nil
for (index,i) in daynames.enumerate() {
if (index == 0){
frame1 = CGRect(x: 1.0, y: -2.0, width: cellWidth , height: cellHeight)
}
else{
frame1 = CGRect(x: CGFloat(prevWidth) , y: -2.0,width:cellWidth , height: cellHeight)
}
if (index < 6){
prevWidth = frame1!.origin.x + cellWidth + distances[index] - SPACING
}
let label = UILabel(frame:frame1!)
label.text = i
label.textAlignment = NSTextAlignment.Center
label.textColor = UIColor.whiteColor()
label.font = UIFont(name:"Helvetica",size:16)
label.backgroundColor = UIColor.blackColor()
// label.center.x = cell_centres[index]
cell.addSubview(label)
}
return cell
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
let label = cell.contentView.viewWithTag(1) as! UILabel
//testing purpose
label.text = String(days[indexPath.row])
if indexPath.row == 0{
cell_centres.removeAll()
}
cell.backgroundColor = UIColor.greenColor()
if indexPath.row < 7{
cell_centres.append(cell.center.x)
print("appending \(cell.center.x)")
}
//code for calc distances between cells
if(prevX == 0){
prevX = cell.center.x
}
else if(indexPath.row < 7){
distance = floor((cell.center.x - prevX) - cellWidth)
print("distance \(distance) ")
distances.append(distance)
prevX = cell.center.x
}
return cell
}
}