Delegate Function Not Being Called - ios

So I am trying to use protocols and delegates to connect two functions so I can perform some operation on a variable a collectionView in this case in a different file.
import Foundation
import UIKit
protocol EventCollectionCellDelegate: NSObjectProtocol {
func setupCollectionView(for eventCollectionView: UICollectionView?)
}
class EventCollectionCell:UICollectionViewCell {
weak var delegate: EventCollectionCellDelegate?
var eventArray = [EventDetails](){
didSet{
self.eventCollectionView.reloadData()
}
}
var enentDetails:Friend?{
didSet{
var name = "N/A"
var total = 0
seperator.isHidden = true
if let value = enentDetails?.friendName{
name = value
}
if let value = enentDetails?.events{
total = value.count
self.eventArray = value
seperator.isHidden = false
}
if let value = enentDetails?.imageUrl{
profileImageView.loadImage(urlString: value)
}else{
profileImageView.image = #imageLiteral(resourceName: "Tokyo")
}
self.eventCollectionView.reloadData()
setLabel(name: name, totalEvents: total)
}
}
let container:UIView={
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 16
view.layer.borderColor = UIColor.lightGray.cgColor
view.layer.borderWidth = 0.3
return view
}()
//profile image view for the user
var profileImageView:CustomImageView={
let iv = CustomImageView()
iv.layer.masksToBounds = true
iv.layer.borderColor = UIColor.lightGray.cgColor
iv.layer.borderWidth = 0.3
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
//will show the name of the user as well as the total number of events he is attending
let labelNameAndTotalEvents:UILabel={
let label = UILabel()
label.textColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
let seperator:UIView={
let view = UIView()
view.backgroundColor = .lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
//collectionview that contains all of the events a specific user will be attensing
let flow = UICollectionViewFlowLayout()
lazy var eventCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flow)
// var eventCollectionView:UICollectionView?
override init(frame: CGRect) {
super.init(frame: frame)
self.setUpCell()
self.setupCollectionView(for: eventCollectionView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupCollectionView(for eventCollectionView: UICollectionView?){
delegate?.setupCollectionView(for: eventCollectionView)
}
}
This is the file that creates a collectionViewCell with a collectionView in it. I am trying to perform some operation on that collectionView using the delegate pattern. My problem is that the delegate function is never called in the accompanying viewController. I feel like I have done everything right but nothing happens in the accompanying vc. Anyone notice what could possibly be wrong.
I have shown the code for the VC below
class FriendsEventsView: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,EventCollectionCellDelegate {
var friends = [Friend]()
var followingUsers = [String]()
var height:CGFloat = 0
var notExpandedHeight : CGFloat = 50
var isExpanded = [Bool]()
//so this is the main collectonview that encompasses the entire view
lazy var mainCollectionView:UICollectionView={
// the flow layout which is needed when you create any collection view
let flow = UICollectionViewFlowLayout()
//setting the scroll direction
flow.scrollDirection = .vertical
//setting space between elements
let spacingbw:CGFloat = 5
flow.minimumLineSpacing = spacingbw
flow.minimumInteritemSpacing = 0
//actually creating collectionview
let cv = UICollectionView(frame: .zero, collectionViewLayout: flow)
//register a cell for that collectionview
cv.register(EventCollectionCell.self, forCellWithReuseIdentifier: "events")
cv.translatesAutoresizingMaskIntoConstraints = false
//changing background color
cv.backgroundColor = .white
//sets the delegate of the collectionView to self. By doing this all messages in regards to the collectionView will be sent to the collectionView or you.
//"Delegates send messages"
cv.delegate = self
//sets the datsource of the collectionView to you so you can control where the data gets pulled from
cv.dataSource = self
//sets positon of collectionview in regards to the regular view
cv.contentInset = UIEdgeInsetsMake(spacingbw, 0, spacingbw, 0)
return cv
}()
lazy var eventCollectionView:UICollectionView={
let flow = UICollectionViewFlowLayout()
flow.scrollDirection = .vertical
let spacingbw:CGFloat = 5
flow.minimumLineSpacing = 0
flow.minimumInteritemSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: flow)
//will register the eventdetailcell
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .white
cv.register(EventDetailsCell.self, forCellWithReuseIdentifier: "eventDetails")
cv.delegate = self
cv.dataSource = self
cv.contentInset = UIEdgeInsetsMake(spacingbw, 0, spacingbw, 0)
cv.showsVerticalScrollIndicator = false
cv.bounces = false
return cv
}()
func setupCollectionView(for eventCollectionView: UICollectionView?) {
print("Attempting to create collectonView")
eventCollectionView?.backgroundColor = .blue
}
//label that will be displayed if there are no events
let labelNotEvents:UILabel={
let label = UILabel()
label.textColor = .lightGray
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = UIFont.italicSystemFont(ofSize: 14)
label.text = "No events found"
label.isHidden = true
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
//will set up all the views in the screen
self.setUpViews()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "close_black").withRenderingMode(.alwaysOriginal), style: .done, target: self, action: #selector(self.goBack))
}
func setUpViews(){
//well set the navbar title to Friends Events
self.title = "Friends Events"
view.backgroundColor = .white
//adds the main collection view to the view and adds proper constraints for positioning
view.addSubview(mainCollectionView)
mainCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
mainCollectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
mainCollectionView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
mainCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
//adds the label to alert someone that there are no events to the collectionview and adds proper constrains for positioning
mainCollectionView.addSubview(labelNotEvents)
labelNotEvents.centerYAnchor.constraint(equalTo: mainCollectionView.centerYAnchor, constant: 0).isActive = true
labelNotEvents.centerXAnchor.constraint(equalTo: mainCollectionView.centerXAnchor, constant: 0).isActive = true
//will fetch events from server
self.fetchEventsFromServer()
}
// MARK: CollectionView Datasource for maincollection view
//woll let us know how many cells are being displayed
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(friends.count)
isExpanded = Array(repeating: false, count: friends.count)
return friends.count
}
//will control the size of the cell that is displayed in the containerview
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
height = 100
let event = friends[indexPath.item]
if let count = event.events?.count,count != 0{
height += (CGFloat(count*40)+10)
}
return CGSize(width: collectionView.frame.width, height: height)
}
//will do the job of effieicently creating cells for the eventcollectioncell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "events", for: indexPath) as! EventCollectionCell
cell.delegate = self
cell.enentDetails = friends[indexPath.item]
cell.eventCollectionView = eventCollectionView
return cell
}
}
I have cut the code down to what I believe is needed to answer the question for simplicity. Any help is appreciated

You set delegate after setupCollectionView. In your case, you can't call setupCollectionView before you set delegate, because setupCollectionView called in init

Related

UIImageView not resizing as circle and UILabel not resizing within StackView and Custom Collection Cell

I am trying to resize my UIImageView as a circle, however; every time I try to resize the UIImageView, which is inside a StackView along with the UILabel, I keep on ending up with a more rectangular shape. Can someone show me where I am going wrong I have been stuck on this for days? Below is my code, and what it's trying to do is add the image with the label at the bottom, and the image is supposed to be round, and this is supposed to be for my collection view controller.
custom collection view cell
import UIKit
class CarerCollectionViewCell: UICollectionViewCell {
static let identifier = "CarerCollectionViewCell"
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.frame = CGRect(x: 0, y: 0, width: 20, height: 20);
//imageView.center = imageView.superview!.center;
imageView.contentMode = .scaleAspectFill
imageView.layer.borderWidth = 4
imageView.layer.masksToBounds = false
imageView.layer.borderColor = UIColor.orange.cgColor
imageView.layer.cornerRadius = imageView.frame.height / 2
return imageView
}()
private let carerNamelabel: UILabel = {
let carerNamelabel = UILabel()
carerNamelabel.layer.masksToBounds = false
carerNamelabel.font = .systemFont(ofSize: 12)
carerNamelabel.textAlignment = .center
carerNamelabel.layer.frame = CGRect(x: 0, y: 0, width: 50, height: 50);
return carerNamelabel
}()
private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.layer.masksToBounds = false
stackView.axis = .vertical
stackView.alignment = .center
stackView.backgroundColor = .systemOrange
stackView.distribution = .fillProportionally
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureContentView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func configureContentView() {
imageView.clipsToBounds = true
stackView.clipsToBounds = true
carerNamelabel.clipsToBounds = true
contentView.addSubview(stackView)
configureStackView()
}
private func configureStackView() {
allContraints()
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(carerNamelabel)
}
private func allContraints() {
setStackViewConstraint()
}
private func setStackViewConstraint() {
stackView.widthAnchor.constraint(equalTo: contentView.widthAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: contentView.heightAnchor).isActive = true
stackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
public func configureImage(with imageName: String, andImageName labelName: String) {
imageView.image = UIImage(named: imageName)
carerNamelabel.text = labelName
}
override func layoutSubviews() {
super.layoutSubviews()
stackView.frame = contentView.bounds
}
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
carerNamelabel.text = nil
}
}
Below here is my code for the CollectionViewControler
custom collection view
import UIKit
class CarerViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: 120, height: 120)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(CarerCollectionViewCell.self, forCellWithReuseIdentifier: CarerCollectionViewCell.identifier)
collectionView.showsVerticalScrollIndicator = false
collectionView.backgroundColor = .clear
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for `collectionView`
NSLayoutConstraint.activate([
collectionView.widthAnchor.constraint(equalTo: view.widthAnchor),
collectionView.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor, constant: 600),
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CarerCollectionViewCell.identifier, for: indexPath) as! CarerCollectionViewCell
cell.configureImage(with: "m7opt04g_ms-dhoni-afp_625x300_06_July_20", andImageName: "IMAGE NO. 1")
return cell
}
}
Can somebody show or point to me what I am doing wrong, thank you
This is what I am trying to achieve
UIStackView could be tricky sometimes, you can embed your UIImageView in a UIView, and move your layout code to viewWillLayoutSubviews(), this also implys for the UILabel embed that also inside a UIView, so the containers UIViews will have a static frame for the UIStackViewto be layout correctly and whats inside them will only affect itself.

How to add constraints to a collection view cell once the cell is selected?

I am trying to create a feature programmatically so that when a user selects a cell in the collection view the app keeps a count of the image selected and adds it as an overlay. I am also wanting to add the video duration to the bottom of the image if the selection is a video. I know my problem is in my constraints. You can see in the image example below that I am trying to add the count to the top left of the collection view cell, but also when the user deselects a cell the count adjusts so for example if the number 2 in the image below was deselected the number 3 would become 2. For the most part I think I have the code working but I cannot get the constraints to work. With the current configuration I am getting an error (see below) but I do not even know where to begin with this problem.
"Unable to activate constraint with anchors because they have
no common ancestor. Does the constraint or its anchors reference
items in different view hierarchies? That's illegal."
CollectionView:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
cell.commonInit()
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
//Not sure what to put here
}
}
Overlay
class CustomAssetCellOverlay: UIView {
let countSize = CGSize(width: 40, height: 40)
lazy var circleView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.layer.cornerRadius = self.countSize.width / 2
view.alpha = 0.4
return view
}()
let countLabel: UILabel = {
let label = UILabel()
let font = UIFont.preferredFont(forTextStyle: .headline)
label.font = UIFont.systemFont(ofSize: font.pointSize, weight: UIFont.Weight.bold)
label.textAlignment = .center
label.textColor = .white
label.adjustsFontSizeToFitWidth = true
return label
}()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
private func commonInit() {
addSubview(circleView)
addSubview(countLabel)
//***** START - UPDATED BASED ON SUGGESTION IN COMMENTS******
countLabel.translatesAutoresizingMaskIntoConstraints = false
//***** END - UPDATED BASED ON SUGGESTION IN COMMENTS******
countLabel.centerXAnchor.constraint(equalTo: circleView.centerXAnchor).isActive = true
countLabel.centerYAnchor.constraint(equalTo: circleView.centerYAnchor).isActive = true
}
}
Collection View Cell
var img = UIImageView()
var overlayView = UIView()
var asset: PHAsset? {
didSet {}
}
var isVideo: Bool = false {
didSet {
durationLabel.isHidden = !isVideo
}
}
override var isSelected: Bool {
didSet { overlay.isHidden = !isSelected }
}
var imageView: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.contentMode = .scaleAspectFill
view.backgroundColor = UIColor.gray
return view
}()
var count: Int = 0 {
didSet { overlay.countLabel.text = "\(count)" }
}
var duration: TimeInterval = 0 {
didSet {
let hour = Int(duration / 3600)
let min = Int((duration / 60).truncatingRemainder(dividingBy: 60))
let sec = Int(duration.truncatingRemainder(dividingBy: 60))
var durationString = hour > 0 ? "\(hour)" : ""
durationString.append(min > 0 ? "\(min):" : ":")
durationString.append(String(format: "%02d", sec))
durationLabel.text = durationString
}
}
let overlay: CustomAssetCellOverlay = {
let view = CustomAssetCellOverlay()
view.isHidden = true
return view
}()
let durationLabel: UILabel = {
let label = UILabel()
label.preferredMaxLayoutWidth = 80
label.backgroundColor = .gray
label.textColor = .white
label.textAlignment = .right
label.font = UIFont.boldSystemFont(ofSize: 20)
return label
}()
func commonInit() {
addSubview(imageView)
imageView.addSubview(overlay)
imageView.addSubview(durationLabel)
imageView.translatesAutoresizingMaskIntoConstraints = false
//***** START - UPDATED BASED ON SUGGESTION IN COMMENTS******
overlay.translatesAutoresizingMaskIntoConstraints = false
overlayView.translatesAutoresizingMaskIntoConstraints = false
//***** END - UPDATED BASED ON SUGGESTION IN COMMENTS******
NSLayoutConstraint.activate([
overlay.topAnchor.constraint(equalTo: imageView.topAnchor),
overlay.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),
overlay.leftAnchor.constraint(equalTo: imageView.leftAnchor),
overlay.rightAnchor.constraint(equalTo: imageView.rightAnchor),
overlayView.centerXAnchor.constraint(equalTo: overlay.centerXAnchor),
overlayView.centerYAnchor.constraint(equalTo: overlay.centerYAnchor),
overlayView.widthAnchor.constraint(equalToConstant: 80.0),
overlayView.heightAnchor.constraint(equalToConstant: 80.0),
]
)
}
//Some other stuff

UICollectionCellView delegate not being fired

A UICollectionView I have created doesn't want to fire a custom cell delegate.
The cells are displaying, and the test button accepts presses.
I have debugged the code and the cell.delegate is being assigned self.
I have researched this on google and I have come up with nothing. I think my code is ok but I must be missing something?
I would really appreciate any help.
//HomeControllerDelegateTesting.swift
import UIKit
class HomeControllerDelegateTesting: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, PostCellOptionsDelegate {
var collectionView: UICollectionView! = nil
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: self.createLayout())
self.collectionView.register(PostCell.self, forCellWithReuseIdentifier: PostCell.reuseIdentifier)
self.collectionView.backgroundColor = .systemBackground
self.view.addSubview(collectionView)
self.collectionView.dataSource = self
self.collectionView.delegate = self
}
private func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(300))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PostCell.reuseIdentifier, for: indexPath) as? PostCell else { fatalError("Cannot create cell") }
cell.postTextLabel.text = "Test"
cell.delegate = self
//Test to eliminiate the button accepts events
//cell.optionsButtons.addTarget(self, action: #selector(test), for: .touchUpInside)
return cell
}
#objc func handlePostOptions(cell: PostCell) {
print("123")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
}
// PostCell.swift
import UIKit
protocol PostCellOptionsDelegate: class {
func handlePostOptions(cell: PostCell)
}
class PostCell: UICollectionViewCell {
static let reuseIdentifier = "list-cell-reuse-identifier"
var delegate: PostCellOptionsDelegate?
let usernameLabel: UILabel = {
let label = UILabel()
label.text = "Username"
label.font = .boldSystemFont(ofSize: 15)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let postImageView: UIImageView = {
let control = UIImageView()
control.translatesAutoresizingMaskIntoConstraints = false
control.contentMode = .scaleAspectFill
control.clipsToBounds = true
return control
}()
let postTextLabel: UILabel = {
let label = UILabel()
label.text = "Post text spanning multiple lines"
label.font = .systemFont(ofSize: 15)
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// private let optionsButtons: UIButton = {
// let control = UIButton.systemButton(with: #imageLiteral(resourceName: "post_options"), target: self, action: #selector(handleOptions))
// control.translatesAutoresizingMaskIntoConstraints = false
// return control
// }()
let optionsButtons: UIButton = {
let control = UIButton(type: .system)
control.setTitle("Test", for: .normal)
control.addTarget(self, action: #selector(handleOptions), for: .touchUpInside)
control.translatesAutoresizingMaskIntoConstraints = false
return control
}()
#objc fileprivate func handleOptions() {
print("Handle options button")
delegate?.handlePostOptions(cell: self)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupComponents()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupComponents() {
self.optionsButtons.heightAnchor.constraint(equalToConstant: 50).isActive = true
let labelStack = UIStackView(arrangedSubviews: [optionsButtons])
labelStack.translatesAutoresizingMaskIntoConstraints = false
labelStack.axis = .horizontal
labelStack.alignment = .fill
labelStack.distribution = .fill
labelStack.isLayoutMarginsRelativeArrangement = true
labelStack.layoutMargins.left = 16
labelStack.layoutMargins.right = 16
labelStack.layoutMargins.top = 16
labelStack.layoutMargins.bottom = 16
let postTextLabelStack = UIStackView(arrangedSubviews: [postTextLabel])
postTextLabelStack.translatesAutoresizingMaskIntoConstraints = false
postTextLabelStack.axis = .vertical
postTextLabelStack.alignment = .fill
postTextLabelStack.distribution = .fill
postTextLabelStack.isLayoutMarginsRelativeArrangement = true
postTextLabelStack.layoutMargins.left = 16
postTextLabelStack.layoutMargins.right = 16
postTextLabelStack.layoutMargins.top = 16
postTextLabelStack.layoutMargins.bottom = 16
let stack = UIStackView(arrangedSubviews: [labelStack, postImageView, postTextLabelStack])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.alignment = .fill
stack.distribution = .fill
stack.backgroundColor = .blue
self.addSubview(stack)
//postImageView.heightAnchor.constraint(equalTo: postImageView.widthAnchor).isActive = true
stack.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
stack.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
stack.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
}
Change your button declaration to this:
// make this a lazy var
lazy var optionsButtons: UIButton = {
let control = UIButton(type: .system)
control.setTitle("Test", for: .normal)
// add self. to the selector
control.addTarget(self, action: #selector(self.handleOptions), for: .touchUpInside)
control.translatesAutoresizingMaskIntoConstraints = false
return control
}()
The issue is that when the optionsButtons is created the self is still not initialized. Hence you need to make the button lazy var so that it could be loaded lazily when the optionsButtons is called and the UICollectionViewCell is initialized. Adding the target before self is initialized doesn't work.
To fix your issue, modify your optionsButtons declaration to lazy var, like this:
lazy var optionsButtons: UIButton = {
OR
Add the target after the UICollectionViewCell is initialized, like this:
override init(frame: CGRect) {
super.init(frame: frame)
optionsButtons.addTarget(self, action: #selector(handleOptions), for: .touchUpInside)
setupComponents()
}

UIButton does not function in collectionView cell

I am trying to create this collection view cells with kinda paging behavior. In every cell; I made a card flip animation. But flip function does not work. I know that function is OK because; before I create these UICollectionViewCell model, I tried everything in UICollectionView itself and it worked out perfect. But since I need paging behavior, I need multiple pages with card view inside and in every cell when user tabs the card it should flip. So, after I migrated all the code from CollectionView to CollectionViewCell; the card view stopped flipping. Even print(...) statement does not return in Xcode. So I guess Xcode doesn't sense user touch. Anyway, I am so junior so I ll be appreciated if someone solves this out. Here is the code for my collectionView:
import UIKit
class AK11ViewController: AltKategoriViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {
var ak11cv: UICollectionView!
private let ak11CellId = "ak11CellId"
let image1Names = ["bear_first", "heart_second", "leaf_third"]
override func viewDidLoad() {
super.viewDidLoad()
background()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return image1Names.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = ak11cv.dequeueReusableCell(withReuseIdentifier: ak11CellId, for: indexPath) as! AK11PageCell
//cell.translatesAutoresizingMaskIntoConstraints = false
let image1Name = image1Names[indexPath.item]
cell.cardView1.image = UIImage(named: image1Name)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
private func background() {
tabBarController?.tabBar.tintColor = UIColor.white
tabBarController?.tabBar.isTranslucent = false
navigationController?.navigationBar.prefersLargeTitles = false
navigationController?.navigationBar.isTranslucent = true
let ak11Layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
ak11Layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
ak11Layout.scrollDirection = .horizontal
ak11cv = UICollectionView(frame: self.view.frame, collectionViewLayout: ak11Layout)
ak11cv.dataSource = self
ak11cv.delegate = self
ak11cv.isPagingEnabled = true
ak11cv.isUserInteractionEnabled = true
ak11cv.backgroundColor = UIColor.brown
ak11cv.register(AK11PageCell.self, forCellWithReuseIdentifier: ak11CellId)
view.addSubview(ak11cv)
}
}
And here is the code for my collectionViewCell:
import UIKit
class AK11PageCell: UICollectionViewCell {
let mainCardView: UIView = {
let mcv = UIView()
mcv.translatesAutoresizingMaskIntoConstraints = false
mcv.isUserInteractionEnabled = true
return mcv
}()
let frontContainerView: UIView = {
let fcv = UIView()
fcv.translatesAutoresizingMaskIntoConstraints = false
fcv.backgroundColor = .blue
fcv.isUserInteractionEnabled = true
return fcv
}()
let backContainerView: UIView = {
let bcv = UIView()
bcv.translatesAutoresizingMaskIntoConstraints = false
bcv.backgroundColor = .purple
bcv.isUserInteractionEnabled = true
//bcv.isHidden = true
return bcv
}()
let pageControlContainerView: UIView = {
let pcv = UIView()
pcv.translatesAutoresizingMaskIntoConstraints = false
pcv.backgroundColor = .green
pcv.isUserInteractionEnabled = false
return pcv
}()
var cardView1: UIImageView = {
let cv1 = UIImageView()
cv1.translatesAutoresizingMaskIntoConstraints = false
cv1.contentMode = .scaleAspectFit
cv1.isUserInteractionEnabled = true
cv1.image = UIImage(named: "bear_first")
return cv1
}()
var cardView2: UIImageView = {
let cv2 = UIImageView()
cv2.translatesAutoresizingMaskIntoConstraints = false
cv2.contentMode = .scaleAspectFit
cv2.isUserInteractionEnabled = true
cv2.image = UIImage(named: "heart_second")
return cv2
}()
let flipToBack: UIButton = {
let ftb = UIButton(type: .system)
ftb.isUserInteractionEnabled = true
ftb.translatesAutoresizingMaskIntoConstraints = false
ftb.addTarget(self, action: #selector(flip), for: .touchUpInside)
return ftb
}()
let flipToFront: UIButton = {
let ftf = UIButton(type: .system)
ftf.isUserInteractionEnabled = true
ftf.translatesAutoresizingMaskIntoConstraints = false
ftf.addTarget(self, action: #selector(flip), for: .touchUpInside)
return ftf
}()
var flippedCard = false
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.cyan
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate func setupViews() {
addSubview(mainCardView)
mainCardView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: frame.width * 0.1).isActive = true
mainCardView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: frame.width * -0.1).isActive = true
mainCardView.topAnchor.constraint(equalTo: topAnchor, constant: frame.height * 0.05).isActive = true
mainCardView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: frame.height * -0.25).isActive = true
mainCardView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
mainCardView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
mainCardView.addSubview(frontContainerView)
frontContainerView.leadingAnchor.constraint(equalTo: mainCardView.leadingAnchor).isActive = true
frontContainerView.trailingAnchor.constraint(equalTo: mainCardView.trailingAnchor).isActive = true
frontContainerView.topAnchor.constraint(equalTo: mainCardView.topAnchor).isActive = true
frontContainerView.bottomAnchor.constraint(equalTo: mainCardView.bottomAnchor).isActive = true
frontContainerView.centerXAnchor.constraint(equalTo: mainCardView.centerXAnchor).isActive = true
frontContainerView.centerYAnchor.constraint(equalTo: mainCardView.centerYAnchor).isActive = true
mainCardView.addSubview(backContainerView)
backContainerView.leadingAnchor.constraint(equalTo: mainCardView.leadingAnchor).isActive = true
backContainerView.trailingAnchor.constraint(equalTo: mainCardView.trailingAnchor).isActive = true
backContainerView.topAnchor.constraint(equalTo: mainCardView.topAnchor).isActive = true
backContainerView.bottomAnchor.constraint(equalTo: mainCardView.bottomAnchor).isActive = true
backContainerView.centerXAnchor.constraint(equalTo: mainCardView.centerXAnchor).isActive = true
backContainerView.centerYAnchor.constraint(equalTo: mainCardView.centerYAnchor).isActive = true
frontContainerView.addSubview(cardView1)
cardView1.centerXAnchor.constraint(equalTo: frontContainerView.centerXAnchor).isActive = true
cardView1.centerYAnchor.constraint(equalTo: frontContainerView.centerYAnchor).isActive = true
cardView1.leadingAnchor.constraint(equalTo: frontContainerView.leadingAnchor).isActive = true
cardView1.trailingAnchor.constraint(equalTo: frontContainerView.trailingAnchor).isActive = true
cardView1.topAnchor.constraint(equalTo: frontContainerView.topAnchor).isActive = true
cardView1.bottomAnchor.constraint(equalTo: frontContainerView.bottomAnchor).isActive = true
frontContainerView.addSubview(flipToBack)
flipToBack.centerXAnchor.constraint(equalTo: frontContainerView.centerXAnchor).isActive = true
flipToBack.centerYAnchor.constraint(equalTo: frontContainerView.centerYAnchor).isActive = true
flipToBack.leadingAnchor.constraint(equalTo: frontContainerView.leadingAnchor).isActive = true
flipToBack.trailingAnchor.constraint(equalTo: frontContainerView.trailingAnchor).isActive = true
flipToBack.topAnchor.constraint(equalTo: frontContainerView.topAnchor).isActive = true
flipToBack.bottomAnchor.constraint(equalTo: frontContainerView.bottomAnchor).isActive = true
backContainerView.addSubview(cardView2)
cardView2.centerXAnchor.constraint(equalTo: backContainerView.centerXAnchor).isActive = true
cardView2.centerYAnchor.constraint(equalTo: backContainerView.centerYAnchor).isActive = true
cardView2.leadingAnchor.constraint(equalTo: backContainerView.leadingAnchor).isActive = true
cardView2.trailingAnchor.constraint(equalTo: backContainerView.trailingAnchor).isActive = true
cardView2.topAnchor.constraint(equalTo: backContainerView.topAnchor).isActive = true
cardView2.bottomAnchor.constraint(equalTo: backContainerView.bottomAnchor).isActive = true
backContainerView.addSubview(flipToFront)
flipToFront.centerXAnchor.constraint(equalTo: backContainerView.centerXAnchor).isActive = true
flipToFront.centerYAnchor.constraint(equalTo: backContainerView.centerYAnchor).isActive = true
flipToFront.leadingAnchor.constraint(equalTo: backContainerView.leadingAnchor).isActive = true
flipToFront.trailingAnchor.constraint(equalTo: backContainerView.trailingAnchor).isActive = true
flipToFront.topAnchor.constraint(equalTo: backContainerView.topAnchor).isActive = true
flipToFront.bottomAnchor.constraint(equalTo: backContainerView.bottomAnchor).isActive = true
addSubview(pageControlContainerView)
pageControlContainerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
pageControlContainerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
pageControlContainerView.bottomAnchor.constraint(equalTo: mainCardView.bottomAnchor, constant: 20).isActive = true
pageControlContainerView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.075).isActive = true
}
#objc func flip() {
print("tabbed")
flippedCard = !flippedCard
let fromView = flippedCard ? backContainerView : frontContainerView
let toView = flippedCard ? frontContainerView : backContainerView
UIView.transition(from: fromView, to: toView, duration: 0.5, options: [.transitionFlipFromRight, .showHideTransitionViews])
}
}
Please ignore my data model because I did not set it yet; I just want my mainCardView, which is super view for frontCardView and backCardView, to flip. But any suggestions for other stuff are also welcomed.
Thank you!
I believe, to get your button action / target to register correctly, you need to declare them as lazy:
lazy var flipToBack: UIButton = {
lazy var flipToFront: UIButton = {
That should solve the tap issue.

Why UICollectionView is not responding at all?

I'm setting up a UICollectionView inside a ViewController. However, it won't respond to any user interaction, didSelectItemAt function is not getting called and I am unable to scroll it.
I have set the DataSource and Delegate properly inside the viewDidLoad(). Here is my code:
class WelcomeController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let padding: CGFloat = 30
var sources = [Source]()
let sourceCellId = "sourceCellId"
func fetchSources() {
ApiSourceService.sharedInstance.fetchSources() { (root: Sources) in
self.sources = root.source
self.sourceCollectionView.reloadData()
}
}
let backgroundImage: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "background")
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
iv.clipsToBounds = false
return iv
}()
let overlayView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
return view
}()
let logo: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "mylogo")
iv.translatesAutoresizingMaskIntoConstraints = false
iv.clipsToBounds = false
return iv
}()
let defaultButton: UIButton = {
let ub = UIButton()
ub.translatesAutoresizingMaskIntoConstraints = false
ub.setTitle("Inloggen met E-mail", for: .normal)
ub.setImage(UIImage(named: "envelope"), for: .normal)
ub.imageEdgeInsets = UIEdgeInsetsMake(15, 0, 15, 0)
ub.imageView?.contentMode = .scaleAspectFit
ub.contentHorizontalAlignment = .left
ub.titleLabel?.font = UIFont.init(name: "Raleway-Regular", size: 16)
ub.backgroundColor = .cuOrange
return ub
}()
let continueWithoutButton: UIButton = {
let ub = UIButton()
ub.translatesAutoresizingMaskIntoConstraints = false
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: "Doorgaan zonder in te loggen")
let textRange = NSMakeRange(0, attributedString.length)
attributedString.setColor(color: .cuOrange, forText: "Doorgaan")
ub.setAttributedTitle(attributedString, for: .normal)
ub.contentHorizontalAlignment = .center
ub.titleLabel?.font = UIFont.init(name: "Raleway-Regular", size: 16)
ub.titleLabel?.textColor = .white
return ub
}()
let sourceCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .white
return cv
}()
let termsButton: UIButton = {
let ub = UIButton()
ub.translatesAutoresizingMaskIntoConstraints = false
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: "Met het maken van een account \nof bij inloggen, ga ik akkoord \nmet servicevoorwaarden.")
let textRange = NSMakeRange(0, attributedString.length)
attributedString.setColor(color: .cuOrange, forText: "servicevoorwaarden")
ub.setAttributedTitle(attributedString, for: .normal)
ub.contentHorizontalAlignment = .center
ub.contentVerticalAlignment = .bottom
ub.titleLabel?.lineBreakMode = .byWordWrapping
ub.titleLabel?.font = UIFont.init(name: "Raleway-Regular", size: 14)
ub.titleLabel?.textColor = .white
ub.titleLabel?.textAlignment = .center
return ub
}()
override func viewDidLoad() {
super.viewDidLoad()
fetchSources()
sourceCollectionView.dataSource = self
sourceCollectionView.delegate = self
sourceCollectionView.register(SourceCell.self, forCellWithReuseIdentifier: sourceCellId)
view.addSubview(backgroundImage)
view.addSubview(overlayView)
backgroundImage.addSubview(termsButton)
backgroundImage.addSubview(logo)
backgroundImage.addSubview(sourceCollectionView)
backgroundImage.widthAnchor.constraint(lessThanOrEqualToConstant: 375).isActive = true
backgroundImage.heightAnchor.constraint(lessThanOrEqualToConstant: 667).isActive = true
backgroundImage.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
backgroundImage.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
logo.centerXAnchor.constraint(equalTo: backgroundImage.centerXAnchor).isActive = true
logo.topAnchor.constraint(equalTo: backgroundImage.topAnchor, constant: padding).isActive = true
logo.widthAnchor.constraint(equalToConstant: 107).isActive = true
logo.heightAnchor.constraint(equalToConstant: 100).isActive = true
sourceCollectionView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
sourceCollectionView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -(padding*1.5)).isActive = true
sourceCollectionView.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: padding).isActive = true
sourceCollectionView.bottomAnchor.constraint(equalTo: termsButton.topAnchor, constant: -(padding*2)).isActive = true
termsButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding).isActive = true
termsButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
termsButton.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -(padding*2)).isActive = true
termsButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
sourceCollectionView.reloadData()
}
#objc func closeWelcomeController() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.switchViewControllers()
self.dismiss(animated: true, completion: {
})
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = self.sourceCollectionView.frame.width
let height = CGFloat(50)
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sources.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(sources[indexPath.item].feed_name)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: sourceCellId, for: indexPath) as! SourceCell
cell.preservesSuperviewLayoutMargins = true
cell.source = sources[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("selecting")
}}
Anyone knows what I am doing wrong here?
I hope there isn't just a "dumb" error inside the code why it's not working, but it's been eating my brain for the past few hours.
It seems that it is inappropriate to add sourceCollectionView as a subview in backgroundImage which is UIImageView! Logically, image view doesn't represent a container view (such as UIView UIStackView, UIScrollView) i.e even if the code lets you do that, it still non-sensible; Furthermore, even if adding a subview to an image view is ok (which is not), image views by default are user interaction disabled (userInteractionEnabled is false by default for image views), that's why you are unable to even scroll the collection view, it is a part (subview) of a disabled user interaction component.
In your viewDidLoad(), you are implementing:
backgroundImage.addSubview(termsButton)
backgroundImage.addSubview(logo)
backgroundImage.addSubview(sourceCollectionView)
Don't do this, instead, add them to main view of the view controller:
view.addSubview(termsButton)
view.addSubview(logo)
view.addSubview(sourceCollectionView)
For the purpose of checking if its the reason of the issue, at least do it for the collection view (view.addSubview(sourceCollectionView)).
And for the purpose of organizing the hierarchy of the view (which on is on top of the other), you could use both: bringSubview(toFront:) or sendSubview(toBack:). For instance, after adding the collection view into the view, you might need to:
view.bringSubview(toFront: sourceCollectionView)
to make sure that the collection view is on top of all components.
I see you add sourceCollectionView to subview of backgroundImage. Your backgroundImage is UIImageView so UIImageView doesn't have user interaction except you need to enable it. The problem of your code is
backgroundImage.addSubview(sourceCollectionView)
For my suggestion you should create UIView or other views that can interact with user interaction. It will work fine.

Resources