Inside each collection view cell I have a view which I'd like to make a perfect circle. I'm basing the width of the cells on the main views width using Autolayout.
My problem: some of the views don't have a perfect circle. I'm guessing its a conflict in timing, that I don't have the main views bounds set yet before making these UIViews into circles. Should I draw the cornerRadius at a specific time? Everything else works fine. Thanks!
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize {
return CGSize(width: self.view.frame.width/3, height: 400)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let sessionCell = (collectionView.dequeueReusableCell(withReuseIdentifier: Storyboard.cellID, for: indexPath)) as? DayCollectionViewCell {
sessionCell.circleView.layer.cornerRadius = sessionCell.circleView.frame.width/2
return sessionCell
}
}
You need to set it here
override func layoutSubviews() {
super.layoutSubviews()
self.circleView.layer.cornerRadius = self.circleView.frame.width/2
}
Inside DayCollectionViewCell class
Related
This question already has answers here:
Snap to center of a cell when scrolling UICollectionView horizontally
(16 answers)
Closed last year.
I'm using a horizontal collection view to display various quotes which users can swipe to scroll through. It works fine, but the scroll is more like one long continuous scroll (like Instagram). I would like to change this to have each quote stop in the center of the screen and then the user to swipe again to go to the next quote (more like TikToc). How can I make this happen? I'm new to coding, so I'm not sure if there is a function for this. I tried the func ScrollToitem but don't know how to get it to implement this behavior.
import UIKit
class mainViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var checkcollectionview: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
checkcollectionview.dataSource = self
checkcollectionview.delegate = self
checkcollectionview.collectionViewLayout = UICollectionViewFlowLayout()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
self.checkcollectionview.collectionViewLayout = layout
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return quotes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = checkcollectionview.dequeueReusableCell(withReuseIdentifier: "check", for: indexPath) as! checkCollectionViewCell
cell.setup(with: quotes[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 700, height: 200)
}
}
As recommended by Andrew, I found a solution in a different post (link to solution below). The snap is nice and smooth.
https://stackoverflow.com/a/48572117/17152459
I want to have oval shaped collection cells with width based on the label/text length, but I am having trouble making all the cells look oval. I don't know how to resize based on the label's text length.
I am essentially trying to get something like the blank/pink picture below I saw on another post but the solution didn't work. I have also added what my view controller currently looks like.
1) How do I resize to get oval shape correctly and also 2) why are there longer spaces between some cells and how do I fix that?
Ideal Pic
Current Controller
Storyboard cell
(width of label is set to 150)
class HobbiesViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
#IBOutlet weak var collectionView: UICollectionView!
var items = ["karateeeeeeeeeee", "signup", "last", "madur", "open", "somelongword", "nice", "looooooooong", "karate", "karate","karate", "signup", "last", "madur"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hobbyCell", for: indexPath) as! HobbiesViewCell
cell.label.text = self.items[indexPath.item]
cell.backgroundColor=UIColor.blue //try with different values untill u get the rounded corners
cell.layer.cornerRadius = cell.bounds.size.height / 2
cell.layer.masksToBounds=true
return cell
}
}
extension HobbiesViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hobbyCell", for: indexPath) as! HobbiesViewCell
cell.label.text = items[indexPath.row]
cell.label.sizeToFit()
return CGSize(width: cell.label.frame.width + 10 , height: 70)
}
}
Ans 1. While setting the width of the collection view cell you need to have some minimum width for the cell so that the cell does not get so small that it looks like a circle. Your current height is 70 so that you should keep a condition that if the cell.label.frame.width + 10 is less than 160 then keep the size of the cell as 160.
Ans 2. Collection view itself manages the spacing between the cells according to the frame provided to collection view and the sized to the cell. To set the spacing right may be below link will be helpful to you.
Cell spacing in UICollectionView
From screen shot I can see that your font size also differs from the expected output so may be you can check that also.
Hope this helps you in some way.
I am trying to do a layout as Pinterest uses for their image gallery. But the issue is that I'm returning the image height to the size of the cells and when the images have different heights, it leaves spaces in the cells.
I have walked through lots of articles, but I have not found a simple solution. Is there any simple way we can do this?
Please see this image
class ViewController: UIViewController {
#IBOutlet var collectionVIew: UICollectionView!
var imgData = [#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8")]
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
let img = imgData[indexPath.item]
// 335 is the width of my cell image
return CGSize(width: width/2-15, height: (img.size.height / 335) * width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imgData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFit
return cell
}
}
What you have mentioned can be achieved using flow layout — a layout class provided by UIKit.
This is what you are looking for:
https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest
I hope you can put some effort into reading this very simple post and follow each step carefully.
You are trying to fit non-square images into a square hole. When we do this something has to be given. In this case, I would recommend you make each of your cells a fixed height and set an aspect fill content mode on them so they fill the cell's frame completely. The downside is that some of the larger images will have part of them cut off. The other option is to use the aspect fit content mode which will scale the image to fit without losing a part of the image. This would leave you with the cell spacing again.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
return CGSize(width: width/2-15, height: 100) // fixed height
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFill // Set the scale mode to aspect fill
return cell
}
Alternatively, you can check out some open source libraries that might implement this for you. This one looks promising https://github.com/abdullahselek/ASCollectionView
You can check out a larger list of collection view open-source libraries here https://github.com/vsouza/awesome-ios#collection-view
If you want the cells to have different heights you need to calculate the correct height based on the aspect ratio of the image and the width that is fixed. You calculate the aspect ratio in this case by the height/width when our width is fixed and our height is dynamic. Then multiply that by the width of the cell of the image to get our dynamic cell height.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width / 2 - 15;
let img = imgData[indexPath.item]
return CGSize(width: width, height: (img.size.height / img.size.width) * width)
}
You should set your content mode to aspect fit in this case.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFit // Set the scale mode to aspect fit
return cell
}
Currently I'm using UICollectionView with several cells in it. UICollectionView scrolls horizontally to move between the cells.
My question is : Is there any way to call func(didSelectItemAt) by clicking the cell while its UICollectionView is on scrolling?
Here is gif.
Right after dragging cell to left for scrolling, I click the cell continuously to call func(didSelectItemAt) but it get call only when scrolling is finished. Is it possible to func(didSelectItemAt) to be called instantly right after clicking cell while on scrolling?
Here is my code for collectionView delegate & dataSource.
extension CardViewController : UICollectionViewDelegate,UICollectionViewDataSource {
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = cardCollectionView.dequeueReusableCell(withReuseIdentifier: "card", for: indexPath) as! CardCell
cell.isFront = cellsSelectedStatus[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = cardCollectionView.cellForItem(at: indexPath) as! CardCell
UIView.transition(with: cell, duration: 0.3, options: [.transitionFlipFromLeft,.allowUserInteraction], animations: nil, completion: nil)
cellsSelectedStatus[indexPath.row] = !(cellsSelectedStatus[indexPath.row])
cell.isFront = cellsSelectedStatus[indexPath.row]
print("did select")
}
Collectionview’s dataSource&delegate are called in viewDidLoad.
I know there is allowUserInteraction from UIViewAnimationOptions which allows to click view while its being animated. Is there smilier code for func(didSelectItemAt)?
There’s no networking & UIGestureRecognizers is attached to the cell or collectionview.
So far I've set
isUserInteractionEnabled,
isMultipleTouchEnabled,
allowsMultipleSelection to true for collectionView,
but it still behaves the same.
I’m using Swift3, Xcode9.
Thanks in advance.
Hi I am creating an app which requires a grid of images. As an example I am using a 2x2 grid of images in cells of a UICollectionView. I would like to know how I could decrease the annoying space in the middle and also make them have equal widths and heights to fit the whole screen. I know that I can do this with Interface builder. However, I want to do this programmatically because as the app progresses, there will be different levels and different number of squares - possibly a 10x10 also. I am new to UICollectionViews. I have referenced to many other questions, however most are in Objective C and there few that are in Swift don't seem to work with Swift 3.
Here is my full code:
import UIKit
class GameViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.delegate = self
collectionView.dataSource = self
let collectionViewLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2;
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let image = cell.viewWithTag(1) as! UIImageView
image.image = #imageLiteral(resourceName: "coffee")
return cell;
}
func collectionView(_collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: self.collectionView.frame.size.width/2, height: self.collectionView.frame.size.height/2)
//this method is not getting called
}
}
I am also uploading an image of how it currently is and how I want it to be-
I hope you guys will help me- it will be very useful... Thanks!
I conformed to UICollectionViewDelegateFlowLayout and called sizeforitematindexpath but it did not work.
On adding a breakpoint I realized that the method was not getting called. So is there anything wrong I'm doing here. Also Xcode gives me this warning -
Make sure that:
Confirming to UICollectionViewDelegateFlowLayout.
collectionView's width = screen's width.
Min Spacing for cells is zero. You can change it from the Interface Builder (Select the collectionView -> show size inspector -> set min spacing to 0), or by implementing minimumInteritemSpacingForSectionAt returning zero.
If you want to fill the whole Screen height (I am asking because this is not what provided in your screenshot), collectionView's height = screen's height.
Finally, implement sizeForItemAtIndexPath method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 2, height: 100)
}
Use following code in viewDidLoad
let collectionViewLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0
collectionViewLayout.itemSize = CGSize(width: UIScreen.main.bounds.width/2, height: UIScreen.main.bounds.width/2)
Use "UICollectionViewDelegateFlowLayout" Delegate for set size for CollectionView cell size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width/2, height: UIScreen.main.bounds.width/2)
}
I hope this will helpfull for you.
You can use the following to size the cells:
extension GameViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = self.collectionView.bounds.width / 2
return CGSize(width: cellWidth, height: cellWidth)
}
}
Also, add the following lines at the end of viewDidLoad() to get rid of the unwanted white space:
let collectionViewLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0