UICollectionView Magic Selection - ios

I added collection view and add selection events:
private func addFriendCollectionView() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let cellSize = (mainContainerView?.frame.width)! / 4
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 50, right: 10)
layout.itemSize = CGSize(width: cellSize, height: cellSize)
layout.minimumLineSpacing = 50.00
let positionRectangle = CGRect(x: 5, y: 230, width: (mainContainerView?.frame.width)!, height: 300)
friendsCollection = UICollectionView(frame: positionRectangle, collectionViewLayout: layout)
friendsCollection?.registerClass(FriendCollectionViewCell.self, forCellWithReuseIdentifier: "friendCell")
friendsCollection?.backgroundColor = UIColor.whiteColor()
friendsCollection!.layer.borderColor = UIColor.grayColor().CGColor
friendsCollection!.layer.borderWidth = 0.5
friendsCollection?.tag = 1
friendsCollection?.allowsMultipleSelection = true
friendsCollection?.dataSource = self
friendsCollection?.delegate = self
self.view.addSubview(friendsCollection!)
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if collectionView.tag == 1 {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FriendCollectionViewCell
selectedFriends[indexPath] = cell.friendName.text
cell.selectBlackView.hidden = false
cell.selectedImage.hidden = false
print("\(selectedFriends.count) - \(indexPath)")
} else if collectionView.tag == 2 {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! DateSelectionCollectionViewCell
print("\(cell.weekday.text) and \(indexPath)")
}
}
But selection layer add every cell with offset 9. If i am select 1 selected 10, 19, 28... etc but indexPath change correct

Related

`setCollectionViewLayout` crash on iOS 14.2 but works well on other systems

When I hit the button in iOS 14.2 , it will crash and the console will print the following message:
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: <NSIndexPath: 0xb53fd3951e2cb315> {length = 2, path = 0 - 0})'
terminating with uncaught exception of type NSException
But it works well on other Systems.
here's the code:
import UIKit
enum DeviceListStyle: String {
case list
case flow
}
class ViewController: UIViewController {
var style: DeviceListStyle = .list
private lazy var flowLayout: UICollectionViewFlowLayout = {
let sizeW = (UIScreen.main.bounds.width - 45) / 2
let sizeH = sizeW * 120 / 165
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: sizeW, height: sizeH)
flowLayout.minimumLineSpacing = 15
flowLayout.minimumInteritemSpacing = 15
flowLayout.headerReferenceSize = CGSize(width: UIScreen.main.bounds.width - 30, height: 15)
flowLayout.footerReferenceSize = CGSize(width: UIScreen.main.bounds.width - 30, height: 15)
flowLayout.sectionInset.bottom = 15
return flowLayout
}()
private lazy var listLayout: UICollectionViewFlowLayout = {
let sizeW = UIScreen.main.bounds.width - 30
let sizeH: CGFloat = 70
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: sizeW, height: sizeH)
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 15
flowLayout.headerReferenceSize = CGSize(width: UIScreen.main.bounds.width - 30, height: 15)
flowLayout.footerReferenceSize = CGSize(width: UIScreen.main.bounds.width - 30, height: 15)
flowLayout.sectionInset.bottom = 15
return flowLayout
}()
lazy var collectionView: UICollectionView = {
let layout: UICollectionViewLayout
switch style {
case .list:
layout = listLayout
case .flow:
layout = flowLayout
}
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear
cv.delegate = self
cv.dataSource = self
cv.showsHorizontalScrollIndicator = false
cv.showsVerticalScrollIndicator = false
cv.alwaysBounceVertical = true
cv.alwaysBounceHorizontal = false
cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
lazy var btn: UIButton = {
let btn = UIButton()
btn.center = view.center
btn.frame.size = CGSize(width: 80, height: 50)
btn.titleLabel?.textAlignment = .center
btn.backgroundColor = .black
btn.setTitle("change", for: .normal)
btn.addTarget(self, action: #selector(switchStyle), for: .touchUpInside)
return btn
}()
#objc private func switchStyle() {
switch style {
case .list:
self.collectionView.setCollectionViewLayout(self.flowLayout, animated: true) { [weak self] _ in
guard let self = self else { return }
self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
}
self.style = .flow
case .flow:
self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) { [weak self] _ in
guard let self = self else { return }
self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
}
self.style = .list
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.backgroundColor = .purple
collectionView.frame = view.bounds
view.addSubview(collectionView)
view.addSubview(btn)
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
}

UICollectionView's background is a screenshot of the initial position of the scrollview?

This seems like a weird bug and I have adapted the code to see the bug better. By default the background color of my UICollectionView seems to be .systemBackground, but when I set it to .clear, instead of having a clear background, I have a "ghost" of the starting position of the first items in the scroll as a abckground. What could be happening?
The functions of the UICollectionView protocol:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 30
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LineCell", for: indexPath) as! LineCell
cell.changeSize(indexPath.row)
cell.text.text = String(indexPath.row)
return cell
}
How I'm adding the view:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.estimatedItemSize = CGSize(width: 50, height: 80)
numberPicker = UICollectionView(frame: .zero, collectionViewLayout: layout)
secondsControlHolder.addSubview(numberPicker!)
numberPicker?.delegate = delegate
numberPicker?.dataSource = delegate
numberPicker?.register(LineCell.self, forCellWithReuseIdentifier: "LineCell")
numberPicker?.translatesAutoresizingMaskIntoConstraints = false
numberPicker?.heightAnchor.constraint(equalToConstant: 120).isActive = true
numberPicker?.bottomAnchor.constraint(equalTo: secondsControlHolder.bottomAnchor).isActive = true
numberPicker?.leadingAnchor.constraint(equalTo: secondsControlHolder.leadingAnchor).isActive = true
numberPicker?.trailingAnchor.constraint(equalTo: secondsControlHolder.trailingAnchor).isActive = true
numberPicker?.backgroundColor = .clear
numberPicker?.showsVerticalScrollIndicator = false
numberPicker?.showsHorizontalScrollIndicator = false
numberPicker?.isPagingEnabled = false
numberPicker?.contentInset = UIEdgeInsets(top: 0, left: (delegate.view.frame.size.width)/2, bottom: 0, right: (delegate.view.frame.size.width)/2)
let arrow = UIView()
let arrowTriangle = UIImageView(image: UIImage(systemName: "arrowtriangle.up.fill"))
secondsControlHolder.addSubview(arrow)
secondsControlHolder.addSubview(arrowTriangle)
arrow.translatesAutoresizingMaskIntoConstraints = false
arrow.bottomAnchor.constraint(equalTo: secondsControlHolder.bottomAnchor, constant: -10).isActive = true
arrow.centerXAnchor.constraint(equalTo: secondsControlHolder.centerXAnchor).isActive = true
arrow.heightAnchor.constraint(equalToConstant: 100).isActive = true
arrow.backgroundColor = .label
arrow.widthAnchor.constraint(equalToConstant: 5).isActive = true
arrow.isUserInteractionEnabled = false
arrowTriangle.translatesAutoresizingMaskIntoConstraints = false
arrowTriangle.centerXAnchor.constraint(equalTo: arrow.centerXAnchor).isActive = true
arrowTriangle.topAnchor.constraint(equalTo: secondsControlHolder.bottomAnchor, constant: -130).isActive = true
arrowTriangle.tintColor = .label
arrowTriangle.heightAnchor.constraint(equalToConstant: 23).isActive = true
arrowTriangle.widthAnchor.constraint(equalToConstant: 30).isActive = true
The LineCell class:
import UIKit
class LineCell: UICollectionViewCell {
let lineHolder = UIView()
let text = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
text.text = "test"
text.textColor = .white
lineHolder.translatesAutoresizingMaskIntoConstraints = false
lineHolder.addSubview(text)
text.translatesAutoresizingMaskIntoConstraints = false
text.topAnchor.constraint(equalTo: lineHolder.topAnchor).isActive = true
text.bottomAnchor.constraint(equalTo: lineHolder.bottomAnchor).isActive = true
text.leadingAnchor.constraint(equalTo: lineHolder.leadingAnchor).isActive = true
text.trailingAnchor.constraint(equalTo: lineHolder.trailingAnchor).isActive = true
addSubview(lineHolder)
// layer.backgroundColor = CGColor(red: 100, green: 100, blue: 100, alpha: 1)
// setupLineHolder()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupLineHolder(){
lineHolder.frame = CGRect(x: 0, y: 0, width: 1, height: 80)
lineHolder.backgroundColor = .label
// setupLine()
}
func changeSize(_ index: Int){
if index % 5 == 0 {
lineHolder.frame = CGRect(x: 0, y: 0, width: 50, height: 80)
lineHolder.backgroundColor = .label
}else{
lineHolder.frame = CGRect(x: 0, y: 15, width: 50, height: 50)
lineHolder.backgroundColor = .label.withAlphaComponent(0.7)
}
}
}
Turns out I was calling setupControlsArea() twice. It wasn't just the UICollectionView that was duplicated, but the whole controls area view.

Swift - Change UICollectionViewCell size programmatically

I am struggling to change the size my items in the CollectionView.
Custom Flowlayout class found here:
This is my CollectionView:
class ExampleViewController: UIViewController, UICollectionViewDataSource {
let theCollectionView: UICollectionView = {
let v = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
v.contentInsetAdjustmentBehavior = .always
v.layer.cornerRadius = 30
return v
}()
let columnLayout = FlowLayout(
itemSize: CGSize(width: 50, height: 50),
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 20, left: 20, bottom: 10, right: 20)
)
I tried changing the itemSize but that doesnt do anything.
You didn't assigned you flowLayout to the collectionView either try:
theCollectionView.collectionViewLayout = columnLayout or
First define your layout :
let columnLayout = FlowLayout( ...
Then define your collectionView like this:
let theCollectionView: UICollectionView = {
let v = UICollectionView(frame: CGRect.zero, collectionViewLayout:columnLayout)
Here is Code for CollectionViewCell Size Change
(Note:- Change values according to your Requirement!)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: screenWidth/4, height: screenWidth/4)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
cell2.collectionView2.collectionViewLayout = layout

UICollectionViewCell with AutoLayout not working in iOS 10

I'm trying to create a dynamic UICollectionView whose cells auto-resize based on the text inside it. But for some reasons, my custom UICollectionViewCell won't expand to the full width. I am using SnapKit as AutoLayout and all my views are code-based; no xib or storyboard. Here's a debug view of what I got at the moment:
I want the cell to expand full width and the height to fit whatever the content is. Here's a snippet on my UICollectionViewController
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Home"
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: view.frame.width, height: view.frame.height)
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: 375, height: 250)
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height), collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
collectionView.register(HomeCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
if let c = cell as? HomeCollectionViewCell {
c.contentView.frame = c.bounds
c.contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
configureCell(c, indexPath: indexPath)
}
return cell
}
func configureCell(_ cell: HomeCollectionViewCell, indexPath: IndexPath) {
cell.setText(withTitle: items[(indexPath as NSIndexPath).section].title)
}
And here's a snippet of my custom UICollectionViewCell
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.contentView.translatesAutoresizingMaskIntoConstraints = true
self.contentView.bounds = CGRect(x: 0, y: 0, width: 99999, height: 99999)
createTitle()
}
private func createTitle() {
titleView = TTTAttributedLabel(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 56))
titleView.tag = 1
titleView.numberOfLines = 2
titleView.delegate = self
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleView)
titleView.snp_updateConstraints(closure: {
make in
make.leading.trailing.equalTo(contentView).offset(10)
make.top.equalTo(contentView).offset(10)
make.bottom.equalTo(contentView).offset(-10)
})
}
func setText(withTitle title:String, paragraph: String, image: String) {
let titleAttributed = AttributedString(string: title, attributes: titleStringAttr)
titleView.attributedText = titleAttributed
titleView.sizeToFit()
}
I've spent 3 days just working on this on and on.. Any advice appreciated!
Well it's not really a solution, but the TTTAttributedLabel does some black magic that causes the AutoLayout not to work. So for me, I changed the TTTAttributedLabel to UILabel and it works fine.
FYI I posted similar question on SnapKit Github issues; credits to robertjpayne there for the hint (https://github.com/SnapKit/SnapKit/issues/261)

How do I add subviews to a custom UICollectionViewCell

I have a custom UICollectionViewCell class where I want to add subviews.
My cell class: The SetUpView() method will add all the subvies I need.
import Foundation
import UIKit
class RecipeCell: UICollectionViewCell {
var RecipeImg: UIImage!
var StarRatingImg: UIImage!
var RecipeTitleText = ""
var RecipeTextDescription = ""
var View: UIView!
var ImageContainer: UIImageView!
var FavIcon: UIImageView!
var StarRatingContainer: UIImageView!
var KCAL: UILabel!
var RecipeTitle: UITextView!
var RecipeText: UITextView!
func SetUpView()
{
//DropDown!.backgroundColor = UIColor.blueColor()
self.translatesAutoresizingMaskIntoConstraints = false
//View for recipe
View = UIView(frame: CGRectMake(0, 0, self.frame.width, self.frame.height))
View.backgroundColor = UIColor.whiteColor()
//Recipe image
ImageContainer = UIImageView(frame: CGRectMake(0, 0, View.frame.width, View.frame.height/2))
ImageContainer.image = RecipeImg
ImageContainer.contentMode = .ScaleToFill
//Recipe favorit icon
FavIcon = UIImageView(frame: CGRectMake(ImageContainer.frame.width - 35, 5, 30, 30))
FavIcon.image = UIImage(named: "LikeHeart")
//Star rating image
StarRatingContainer = UIImageView(frame: CGRectMake(10, ImageContainer.frame.height + 5, ImageContainer.frame.width - 20, (View.frame.height/2) * (1/5)))
StarRatingContainer.image = StarRatingImg
StarRatingContainer.contentMode = .ScaleAspectFit
//RecipeTitle container
RecipeTitle = UITextView(frame: CGRectMake(10, StarRatingContainer.frame.height + ImageContainer.frame.height + 10, View.frame.width - 20, 30))
RecipeTitle.font = UIFont(name: "OpenSans-Semibold", size: 12)
//RecipeTitle.backgroundColor = UIColor.redColor()
RecipeTitle.editable = false
RecipeTitle.text = RecipeTitleText
RecipeTitle.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
//RecipeText container
RecipeText = UITextView(frame: CGRectMake(10, StarRatingContainer.frame.height + ImageContainer.frame.height + RecipeTitle.frame.height + 15, View.frame.width - 20, 50))
RecipeText.font = UIFont(name: "OpenSans", size: 12)
//RecipeText.backgroundColor = UIColor.grayColor()
RecipeText.editable = false
RecipeText.text = RecipeTextDescription
RecipeText.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
//KCAL label
KCAL = UILabel(frame: CGRectMake(15, StarRatingContainer.frame.height + ImageContainer.frame.height + RecipeTitle.frame.height + RecipeText.frame.height + 20, 200, 20))
KCAL.text = "420 KCAL. PER. PORTION"
KCAL.font = UIFont(name: "OpenSans-Bold", size: 10)
KCAL.textColor = UIColor(CGColor: "#dc994a".CGColor)
//Adding the views
self.addSubview(View)
View.addSubview(ImageContainer)
View.addSubview(KCAL)
View.addSubview(StarRatingContainer)
View.addSubview(RecipeTitle)
View.addSubview(RecipeText)
ImageContainer.addSubview(FavIcon)
View.bringSubviewToFront(ImageContainer)
}
}
I have a UICollectionView which uses the custom cell class.
I create my UICollectionView in viewDidLoad()
// Create Collection view
layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: screenWidth/MenuViewConst - 1, height: screenWidth - 1)
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
collectionView = UICollectionView(frame: CGRect(x: 0, y: 105, width: self.view.frame.width, height: self.view.frame.height - 150), collectionViewLayout: layout)
collectionView?.tag = 5
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(RecipeCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView!.backgroundColor = UIColor.lightGrayColor()
collectionView!.contentInset.top = 0
In cellForItemAtIndexPath delegate I set up the UICollectionView to use my custom cell class. But I can't call the SetUpView() method from my custom cell class here, because that will just keep adding subviews on subviews. I can't figure out how to add the subviews to the UICollectionViewCell before entering the delegate. Hope you guys can help - Thank you
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! RecipeCell
let recipe = self.RecipeArr[indexPath.row]
cell.backgroundColor = UIColor.grayColor()
cell.layer.borderColor = UIColor.whiteColor().CGColor
cell.layer.borderWidth = 0.5
cell.RecipeImg = UIImage(named: "Burger")
cell.StarRatingImg = UIImage(named: "StarRating")
cell.RecipeTitleText = recipe["name"].string!
cell.RecipeTextDescription = recipe["instruction"].string!
//BAD IDEA!
//cell.SetUpView()
print("new cell")
return cell
}
You need to use init(frame: CGRect) inherited function in the UICollectionViewCell .
class RecipeCell: UICollectionViewCell {
var imageView : UIImageView?
override init(frame: CGRect) {
super.init(frame: frame)
//initialize all your subviews.
imageView = UIImageView()
}
}
also don't forget to register your custom class in the viewDidLoad function
collectionView!.registerClass(RecipeCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
and your collectionview delegate would be like this
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! RecipeCell
cell.imageView.image = UIImage(named:"yourImage.png")
}

Resources