Need fixed size Collection Cell - uicollectionviewcell

I am facing issue with UICollectionView. I want all cell should have fixed size. But I cannot achieve requirement.
I want output like :
And what I am getting is like :
Here is my code :
private var totalColumns: Int = 3
private var cellSpace: CGFloat = 10.0
private var width: CGFloat {
var temp = (CGFloat(self.totalColumns - 1) * self.cellSpace)
temp = (self.collectionPreference?.frame.size.width ?? SCREEN_WIDTH) - temp
temp = temp / CGFloat(totalColumns)
return temp
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: width, height: width)
}
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
// return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
// }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return cellSpace
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return cellSpace
}
Before iOS 13, I can achieve same UI with same code (and at that time I was searching that how can we design cell according to its content). And now in iOS 13, I am struggling to achieve fixed size UI.... Very funny...
What I have taken :
UIImageView constraints :
ULabel constraints :
Can anyone have solution ?

Try this:
override func viewDidLoad() {
super.viewDidLoad()
let cellSize = CGSize(width:80 , height:80) // your cell size
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //.horizontal
layout.itemSize = cellSize
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
layout.minimumLineSpacing = 1.0
layout.minimumInteritemSpacing = 1.0
collectionPreference.setCollectionViewLayout(layout, animated: true)
collectionPreference.reloadData()
}
comment if you have any issue on this.

Related

How to add a Gradient Layer to a Collection View Cell

I am attempting to add a gradient to a collection view cell as seen below:
So far the result I get does not cover the entire cell:
I have a
collection view cell:
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var cellView: UIView!
let diagonalGradient = DiagonalGradient()
override func layoutSubviews() {
super.layoutSubviews()
diagonalGradient.frame = cellView.bounds
}
as well as a Custom Gradient Class:
class DiagonalGradient: UIView {
let gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame:frame)
setupGradient(color: UIColor.red))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupGradient(color: UIColor.red))
}
func setupGradient(color: UIColor ) {
gradient.colors = [
UIColor.clear.cgColor,
color.cgColor
]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.frame = bounds
layer.addSublayer(gradient)
}
}
Lastly, in the story board I assign the class of the cellView to my custom gradient class. How do I fix this?
You're setting the gradient layer frame at the wrong time.
Move it to layoutSubviews():
class DiagonalGradient: UIView {
let gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame:frame)
setupGradient(color: UIColor.red))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupGradient(color: UIColor.red))
}
func setupGradient(color: UIColor ) {
gradient.colors = [
UIColor.clear.cgColor,
color.cgColor
]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
// don't do this here
//gradient.frame = bounds
layer.addSublayer(gradient)
}
override func layoutSubviews() {
super.layoutSubviews()
// do this here
gradient.frame = bounds
}
}

Wrong cell data if fast scroll CollectionView swift 5

Can anybody help me with CollectionView? I parce JSON from some API, and get String values - title and image adress, then assign that Data to custom collectionViewCell in method cellForItem(). BUT than i fast scroll the collection view some cell can be with wrong images, or duplicate images. I also override method prepareForReuse in my custom cell class and set cell.image = UIImage() , but it didnt work. Please help me to understand what wrong)
Sorry for my poor English...
collectionViewController
'''
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Result",
for: indexPath) as! ResultCell
let film = results[indexPath.item]
cell.name.text = film.title
cell.imageView.layer.borderColor = UIColor(white: 0, alpha: 0.3).cgColor
cell.imageView.layer.borderWidth = 2
cell.imageView.layer.cornerRadius = 10
cell.layer.cornerRadius = 10
// setUp image for cell
cell.imageView.image = nil
contentManager.getImage(film.poster.image, cell.imageView)
return cell
}
'''
method get image
'''
func getImage(_ url_str:String, _ imageView:UIImageView) {
let url:URL = URL(string: url_str)!
let session = URLSession.shared
let task = session.dataTask(with: url, completionHandler: {(data, response, error) in
if data != nil {
let image = UIImage(data: data!)
if(image != nil) {
DispatchQueue.main.async(execute: {
imageView.image = image
imageView.alpha = 0
UIView.animate(withDuration: 1, animations: {
imageView.alpha = 1.0
})
})
}
}
})
task.resume()
}
'''
Cell custom class
'''
class ResultCell: UICollectionViewCell {
#IBOutlet var imageView: UIImageView!
#IBOutlet var name : UILabel!
override func prepareForReuse() {
self.imageView.image = UIImage()
self.name.text = nil
super.prepareForReuse()
}
}
'''

Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value , Label

I just included certain firebase pods in my project , before that my project had no errors and it was running just fine , but when I added this code
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
handleNotAuthenticated()
}
private func handleNotAuthenticated(){
if Auth.auth().currentUser == nil{
// Show login
let loginVC = LoginControllerViewController()
loginVC.modalPresentationStyle = .fullScreen
present(loginVC,animated: false)
}
// else do nothing
}
I got an error in my loginVC , following is my loginVC Code:-
override func viewDidLoad() {
super.viewDidLoad()
Label.font = UIFont(name: "LobsterTwo-Bold", size: 35) //I got that fatal error here
LoginOutlet.titleLabel?.font = UIFont(name: "LobsterTwo-Bold", size: 25)
DontHaveButtonO.titleLabel?.font = UIFont(name: "Georgia", size: 20)
// Do any additional setup after loading the view.
}
I tried displaying an empty(new VC) it worked fine but , whenever I use this LoginVC it gives me that above error , I tried cleaning build folder and re-adding the fonts folder ,please help me
Try this code, working for me
#IBOutlet weak var Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
Label.font = LobsterTwo.bold.font(size: 50)
}
}
public enum LobsterTwo: String {
case bold = "LobsterTwo-Bold"
case boldItalic = "LobsterTwo-BoldItalic"
case italic = "LobsterTwo-Italic"
public func font(size: CGFloat) -> UIFont {
return UIFont(name: self.rawValue, size: size)!
}
StoryBoard screenshot

UICollectionView-refreshing: getting each cell more than one time

Hi I'm having the issue that after refreshing my collectionView I am getting each cell twice on
my view. And after refreshing a second time I am getting each cell 3 times. I am also getting this issue when presenting the view with a segue a second time.
How can I fix that?
Can someone help me?
Thank you in advance!!!
import UIKit
import Firebase
class FeedViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private let refreshControl = UIRefreshControl()
#IBOutlet weak var collectionview: UICollectionView!
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
collectionview.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(refreshWeatherData(_:)), for: .valueChanged)
refreshControl.tintColor = UIColor(hex: "#F17D32")
}
#objc private func refreshData(_ sender: Any) {
fetchData()
posts.removeAll()
collectionview.reloadData()
}
private func fetchData() {
getPost()
self.refreshControl.endRefreshing()
}
func getPosts(){
let ref = Database.database().reference()
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
let postsSnap = snap.value as! [String : AnyObject]
for (_,post) in postsSnap {
let posst = Post()
if let author = post["author"] as? String, late date = post["date"], let postID = post["postID"] as? String, let userID = post["userID"] as? String {
posst.date = date
posst.author = author
posst.postID = postID
posst.userID = userID
self.posts.sort(by: {$0.postdateprog > $1.postdateprog})
self.posts.append(posst)
}
self.collectionview.reloadData()
}
})
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(posts.count)
return self.posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath) as! PostCell
cell.authorlabel.text = self.posts[indexPath.row].author
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: self.collectionview.frame.width / 2 - 20, height: self.collectionview.frame.width * 0.7)
}
}
Firebase functions are asynchronous and the code in the closure will be called after the synchronous code runs. In other words, you have this
fetchData()
posts.removeAll()
collectionview.reloadData()
but posts.removeAll() and collectionview.reloadData() will be called before the array is populated.
So here's a truncated overview of the order in which things should be called.
#objc private func refreshData(_ sender: Any) {
self.loadFirebaseData()
}
func loadFirebaseData() {
...
self.posts = []
for (_,post) in postsSnap {
self.posts.append(posst)
}
self.posts.sort(by: {$0.postdateprog > $1.postdateprog}
self.collectionview.reloadData()
}
Notice that I moved the sort and the reloadData outside of the for loop. There's no reason to call those functions over and over as the datasource is being populated. Do it after that task is complete.

Can't get number of children from a snapshot

I can't get the number of children returned by snapshot. I always get Snap ((null)) (null) when I write print(self.newCars) in another position:
class NewCarsViewController: UICollectionViewController {
var firebase: FIRDatabaseReference?
var newCars = FIRDataSnapshot()
override func viewDidLoad() {
super.viewDidLoad()
self.firebase = FIRDatabase.database().reference(fromURL:"https://firebaseURL/").child("featuredCars")
firebase?.observeSingleEvent(of: .value, with: { snapshot in
self.newCars = snapshot
print("halim")
print(self.newCars)
// print(snapshot.childrenCount) // I got the expected number of items
for rest in snapshot.children.allObjects as! [FIRDataSnapshot] {
// print(rest.value ?? "")
}
})
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "NewCarCell", for: indexPath) as UICollectionViewCell
return cell
}
}
That code is working for me.
I would guess you are moving the print(self.newCars) outside of the Firebase block (closure).
It takes time for Firebase to return the data, and that data only becomes valid inside the closure.
If the print statement is outside of the closure, it's running before the data is returned from Firebase and it would be null.

Resources