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

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
}
}

Related

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.

Segmented control to switch collection views

Unable to load viewControllers.
There is no initial selection in segmented controller
when my view loads I want my firstCVC be by default the first viewController
Here is my parent viewController
class covidVC: UIViewController {
private let segmentedController: UISegmentedControl = {
let labelArr = ["Covid19","Symptoms","WHO","Vaccine"]
let seg = UISegmentedControl(items: labelArr)
seg.translatesAutoresizingMaskIntoConstraints = false
seg.tintColor = .gray
seg.addTarget(self, action:#selector(segAction(_:)), for: .valueChanged)
return seg
}()
func viewLoader(){
addChild(FirstCVC())
addChild(SecondCVC())
addChild(ThirdCVC())
addChild(FourthCVC())
self.view.addSubview(FirstCVC().view)
self.view.addSubview(SecondCVC().view)
self.view.addSubview(ThirdCVC().view)
self.view.addSubview(FourthCVC().view)
FirstCVC().didMove(toParent: self)
SecondCVC().didMove(toParent: self)
ThirdCVC().didMove(toParent: self)
FourthCVC().didMove(toParent: self)
FirstCVC().view.frame = self.view.bounds
SecondCVC().view.frame = self.view.bounds
ThirdCVC().view.frame = self.view.bounds
FourthCVC().view.frame = self.view.bounds
}
#objc func segAction(_ segmentedControll: UISegmentedControl){
FirstCVC().view.isHidden = true
SecondCVC().view.isHidden = true
ThirdCVC().view.isHidden = true
FourthCVC().view.isHidden = true
switch segmentedControll.selectedSegmentIndex {
case 0:
FirstCVC().view.isHidden = false
case 1:
SecondCVC().view.isHidden = false
case 2:
ThirdCVC().view.isHidden = false
case 3:
FourthCVC().view.isHidden = false
default:
FirstCVC().view.isHidden = false
}
}
override func viewDidLayoutSubviews() {
view.addSubview(segmentedController)
segmentedController.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
segmentedController.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
viewLoader()
}
}
All four Child view are collection VC. I want them to be independent VCs
I don't want to relaod collection views as they have different rows & cols
class FirstCVC: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate{
// primary horizantal scrollVIew
private let myCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: 365 , height: 300)
layout.sectionInset = UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
let view = UICollectionView(frame: .zero, collectionViewLayout:layout)
view.showsVerticalScrollIndicator = false
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FirstCustomCell", for: indexPath) as! FirstCustomCell
cell.backgroundColor = .red
return cell
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.addSubview(myCollectionView)
myCollectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
myCollectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
myCollectionView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.dataSource = self
myCollectionView.delegate = self
myCollectionView.register(FirstCustomCell.self, forCellWithReuseIdentifier: FirstCustomCell.identifier)
}
}
The Image output I'm getting
You create a different instance each line
addChild(FirstCVC())
addChild(SecondCVC())
addChild(ThirdCVC())
addChild(FourthCVC())
self.view.addSubview(FirstCVC().view)
self.view.addSubview(SecondCVC().view)
self.view.addSubview(ThirdCVC().view)
self.view.addSubview(FourthCVC().view)
FirstCVC().didMove(toParent: self)
SecondCVC().didMove(toParent: self)
ThirdCVC().didMove(toParent: self)
FourthCVC().didMove(toParent: self)
FirstCVC().view.frame = self.view.bounds
SecondCVC().view.frame = self.view.bounds
ThirdCVC().view.frame = self.view.bounds
FourthCVC().view.frame = self.view.bounds
while it should be
let vc1 = FirstCVC()
let vc2 = SecondCVC()
let vc3 = ThirdCVC()
let vc4 = FourthCVC()
addChild(vc1)
addChild(vc2)
addChild(vc3)
addChild(vc4)
self.view.addSubview(vc1.view)
self.view.addSubview(vc2.view)
self.view.addSubview(vc3.view)
self.view.addSubview(vc4.view)
vc1.didMove(toParent: self)
vc2.didMove(toParent: self)
vc3.didMove(toParent: self)
vc4.didMove(toParent: self)
vc1.view.frame = self.view.bounds
vc2.view.frame = self.view.bounds
vc3.view.frame = self.view.bounds
vc4.view.frame = self.view.bounds
Tip : Also you better create an extension instead of repeating the 4 lines for each vc

finding a tableView cells superView

I am trying to create a range slider that has labels representing the sliders handle value. I have the slider enabled but when I try to add the labels to the sliders subview, my app crashes with the error
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
The slider is inside of a tableViewCell and I am initializing this cell inside of the tableView VC with the code below,
if indexPath.section == 2 {
let costRangeCell = AgeRangeCell(style: .default, reuseIdentifier: nil)
let contentView = costRangeCell.rangeSlider.superview!
// my declaration of contentView is where my app is crashing.
costRangeCell.rangeSlider.minimumValue = 0
costRangeCell.rangeSlider.maximumValue = 100
costRangeCell.rangeSlider.lowValue = 0
costRangeCell.rangeSlider.highValue = 100
costRangeCell.rangeSlider.minimumDistance = 20
let lowLabel = UILabel()
contentView.addSubview(lowLabel)
lowLabel.textAlignment = .center
lowLabel.frame = CGRect(x:0, y:0, width: 60, height: 20)
let highLabel = UILabel()
contentView.addSubview(highLabel)
highLabel.textAlignment = .center
highLabel.frame = CGRect(x: 0, y: 0, width: 60, height: 20)
costRangeCell.rangeSlider.valuesChangedHandler = { [weak self] in
let lowCenterInSlider = CGPoint(x:costRangeCell.rangeSlider.lowCenter.x, y: costRangeCell.rangeSlider.lowCenter.y - 30)
let highCenterInSlider = CGPoint(x:costRangeCell.rangeSlider.highCenter.x, y: costRangeCell.rangeSlider.highCenter.y - 30)
let lowCenterInView = costRangeCell.rangeSlider.convert(lowCenterInSlider, to: contentView)
let highCenterInView = costRangeCell.rangeSlider.convert(highCenterInSlider, to: contentView)
lowLabel.center = lowCenterInView
highLabel.center = highCenterInView
lowLabel.text = String(format: "%.1f", costRangeCell.rangeSlider.lowValue)
highLabel.text = String(format: "%.1f", costRangeCell.rangeSlider.highValue)
}
costRangeCell.rangeSlider.addTarget(self, action: #selector(handleMinAgeChange), for: .valueChanged)
let minAge = user?.minSeekingCost ?? SettingsViewController.defaultMinSeekingCost
costRangeCell.rangeLabel.text = " $\(minAge)"
return costRangeCell
}
Is there a different way for me to gain access to the cells range slider superView?
ageRange class,
class AgeRangeCell: UITableViewCell {
let rangeSlider: AORangeSlider = {
let slider = AORangeSlider()
slider.minimumValue = 20
slider.maximumValue = 200
return slider
}()
let rangeLabel: UILabel = {
let label = costRangeLabel()
label.text = "$ "
return label
}()
class costRangeLabel: UILabel {
override var intrinsicContentSize: CGSize {
return .init(width: 80, height: 50)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.isUserInteractionEnabled = true
let overallStackView = UIStackView(arrangedSubviews: [
UIStackView(arrangedSubviews: [rangeLabel, rangeLabel]),
])
overallStackView.axis = .horizontal
overallStackView.spacing = 16
addSubview(overallStackView)
overallStackView.anchor(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor, padding: .init(top: 16, left: 16, bottom: 16, right: 16))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
AORangeSlider is a custom Slider.
Took a look at AORangeSlider...
You want to implement your label tracking inside your custom cell... not in your controller class.
Here's a simple implementation, based on the code you supplied in your question:
class AgeRangeCell: UITableViewCell {
let rangeSlider: AORangeSlider = {
let slider = AORangeSlider()
slider.minimumValue = 0
slider.maximumValue = 100
return slider
}()
let lowLabel = UILabel()
let highLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
lowLabel.textAlignment = .center
lowLabel.frame = CGRect(x:0, y:0, width: 60, height: 20)
highLabel.textAlignment = .center
highLabel.frame = CGRect(x: 0, y: 0, width: 60, height: 20)
[rangeSlider, lowLabel, highLabel].forEach {
contentView.addSubview($0)
}
rangeSlider.translatesAutoresizingMaskIntoConstraints = false
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
rangeSlider.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
rangeSlider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
rangeSlider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
rangeSlider.heightAnchor.constraint(equalToConstant: 40.0),
])
// avoid auto-layout complaints
let c = rangeSlider.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0)
c.priority = UILayoutPriority(rawValue: 999)
c.isActive = true
rangeSlider.valuesChangedHandler = { [weak self] in
guard let `self` = self else {
return
}
let lowCenterInSlider = CGPoint(x:self.rangeSlider.lowCenter.x, y: self.rangeSlider.lowCenter.y - 30)
let highCenterInSlider = CGPoint(x:self.rangeSlider.highCenter.x, y: self.rangeSlider.highCenter.y - 30)
let lowCenterInView = self.rangeSlider.convert(lowCenterInSlider, to: self.contentView)
let highCenterInView = self.rangeSlider.convert(highCenterInSlider, to: self.contentView)
self.lowLabel.center = lowCenterInView
self.highLabel.center = highCenterInView
self.lowLabel.text = String(format: "%.1f", self.rangeSlider.lowValue)
self.highLabel.text = String(format: "%.1f", self.rangeSlider.highValue)
}
}
}
class RangeTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// register your "slider" cell
tableView.register(AgeRangeCell.self, forCellReuseIdentifier: "ageRangeCell")
// register any other cell classes you'll be using
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "plainCell")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 2 {
return 1
}
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ageRangeCell", for: indexPath) as! AgeRangeCell
cell.rangeSlider.minimumValue = 0
cell.rangeSlider.maximumValue = 100
cell.rangeSlider.lowValue = 0
cell.rangeSlider.highValue = 100
cell.rangeSlider.minimumDistance = 20
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "plainCell", for: indexPath)
cell.textLabel?.text = "\(indexPath)"
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section Header: \(section)"
}
}
That code will produce this:

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")
}

UICollectionView Magic Selection

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

Resources