I am using SCLAlertView to create custom alert view. My alert view contains one text field and collection view of coloured cells
Problem is that UICollectionView's didSelectItemAt method is not working. I think problem is because it is like subview. But I can't fix it.
I have one collection view at UIViewController and that method is working. Here's my code
var collectionViewAlert: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
layout.itemSize = CGSize(width: 25, height: 25)
collectionViewAlert = UICollectionView(frame: CGRect(x: 18, y: 10, width: 250, height: 25), collectionViewLayout: layout)
collectionViewAlert.dataSource = self
collectionViewAlert.delegate = self
collectionViewAlert.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "CollCell")
collectionViewAlert.backgroundColor = UIColor.white
}
#IBAction func addCategory(_ sender: Any) {
let alertView = SCLAlertView()
alertView.addTextField("Enter category name")
let subview = UIView(frame: CGRect(x:0,y:0,width:216,height:70))
subview.addSubview(self.collectionViewAlert)
alertView.customSubview = subview
alertView.showEdit("Choose color", subTitle: "This alert view has buttons")
}
let reuseIdentifier = "cell" // also enter this string as the cell identifier in the storyboard
var colors = [UIColor.red, UIColor.yellow, UIColor.green, UIColor.blue, UIColor.cyan]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.colors.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
if (collectionView == self.collectionViewAlert) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollCell", for: indexPath as IndexPath)
cell.backgroundColor = self.colors[indexPath.item]
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath)
cell.backgroundColor = self.colors[indexPath.item]// make cell more visible in our example project
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
}
More screens here: screens
EDIT:
I still not found answer how to solve this problem. I think problem is subview interaction, because delegate method cellForItemAt is invoked on alert show. Someone know how to figure this out? screen from view hierarchy
Thanks for any help.
I have looked into SCLAlertView code. It seems it uses a tap recognizer for dismissing the keyboard.
Tap recognizer can conflict with the tap recognizer used by collection view.
To disable the recognizer in SCLAlertView you can use an appearance object:
let appearance = SCLAlertView.SCLAppearance(
disableTapGesture: true
)
let alertView = SCLAlertView(appearance: appearance)
You can also add extension for CollectionView delegate:
extension ViewController: UICollectionViewDelegate
{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
}
If you have added any UIImageView or UILabel inside UICollectionViewCell make sure you have enabled UserIntraction, because for both UIImageView or UILabel default as false.
setUserIntraction for both UIImageView or UILabel as TRUE.
You need to add collectionview delegate in protocol section. And be sure that you have make outlets of your object.
First of all you need to update like:
class YourViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
}
And then in viewDidLoad:
collectionView.delegate = self
collectionView.dataSource = self
collectionView.reloadData()
And make sure that your objects are properly connected.
Related
I am facing one issue. And I think it might be a weird one. I am using UICollectionViewController and configure it something like this
class ViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.minimumLineSpacing = 10
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width - 20, height: 60)
flowLayout.sectionInset = UIEdgeInsets(
top: 10, left: 10, bottom: 10, right: 10
)
collectionView.register(DemoTakesCollectionViewCell.self, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DemoTakesCollectionViewCell.identifier,
for: indexPath) as? DemoTakesCollectionViewCell else {
fatalError("UICollectionView must be downcasted to CollectionViewCell")
}
return cell
}
}
I did also register a cell for UICollectionView, but its content not showing up somehow. In View Hierarchy, Cell is there but not it's content. What could be a problem here?
Ah... you left out an important piece of information in your question:
"It's xib"
So this line:
collectionView.register(DemoTakesCollectionViewCell.self, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
registers the class -- but the class isn't adding any UI elements.
You need to register the nib:
let nib = UINib(nibName: "DemoTakesCollectionViewCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
That should fix the issue.
I'm having some code to create a UICollectionView on my playground. The code works and previews just fine. Now I'm trying to reuse it into a Xcode Project, I'm not getting any errors but the build on the simulator is just plain white.. Nothing to see at all
Can someone explain the reason of this? :)
import UIKit
//import PlaygroundSupport
class MyViewController : UIViewController {
var myCollectionView:UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView()
view.backgroundColor = .white
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 60, height: 60)
myCollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
myCollectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")
myCollectionView?.backgroundColor = UIColor.white
myCollectionView?.dataSource = self
myCollectionView?.delegate = self
view.addSubview(myCollectionView ?? UICollectionView())
self.view = view
}
}
extension MyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9 // How many cells to display
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath)
myCell.backgroundColor = UIColor.blue
return myCell
}
}
extension MyViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("User tapped on item \(indexPath.row)")
}
}
// Present the view controller in the Live View window
//PlaygroundPage.current.liveView = MyViewController()
Did you go to the storyboard, select the view controller in the document outline, and change the class name from UIViewController to MyViewController in the Identity Inspector? If you don't make that change, Xcode assumes you want to use the default UIViewController, which is blank.
I am using Swift3 and Xcode 8.3.3
I am working on UITableViewCell which hold UICollectionView.
Each CollectionView cell need to perform segue.
I mean, i need to call other ViewController with Navigation Controller. So user can back to Main UITableViewController.
import UIKit
class PastCell: UITableViewCell {
#IBOutlet weak var ptripDtls: UICollectionView!
var logcount:Int = 0
override func awakeFromNib() {
super.awakeFromNib()
ptripDtls.delegate = self
ptripDtls.dataSource = self
logcount = Transfer.tripObj[Transfer.cellpos]["logcount"]! as! Int
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = self.ptripDtls.collectionViewLayout.collectionViewContentSize.width;
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: width-17 , height: 50)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 1
ptripDtls!.collectionViewLayout = layout
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
extension PastCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return logcount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PastTripCollectionCell", for: indexPath) as! PastTripCollectionCell
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Might be here segue function will call
}
}
You have actually multiple choices here:
Closure
Pass a closure inside the UITableViewCell like tapped: (Model) -> Void when you're dequeuing a tableView cell, and specify inside that closure to perform a segue. (it works if all your collection cells will perform a same segue, you can use this mechanism to propagate your callback if different segue's should get called)
Delegate
Define a custom delegate, to inform your viewController about CollectoinView's DidSelectItem method, and then perform Segue accordingly.
Notification
Trigger a Notification throughout the system which only your ViewController listens to that notification, and perform your segue accordingly. (This isn't actually a good solution, just pointing out that it's possible)
For swift 4 Use perform segue inside didSelectRowAtIndexPath if you are using segue
self.performSegue(withIdentifier: "YourSegueIdentifier", sender: nil)
and use Push if you are using only navigation controller.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "YourVCIdentifier") as! YourVC
self.navigationController?.pushViewController(vc, animated: true)
Assume I have a user cell. I need to start a ChatViewController for this User. So, I have UICollectionView which presented as raws (1 cell = 1 row). How to segue to ChatViewController from cell?
I don't use InterfaceBuilder at all. Only through code way.
I planned to use didSelectItemAt but I don't have enough swift programming skills to figure out how to use this function for my purpose. When I try to write prepare for segue or performSegue with Identifier inside didSelectItemAt function the Xcode doesn't provide proper autocomplete. My Code is below:
import UIKit
class MyList: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
override init(frame: CGRect) {
super .init(frame: frame)
setUpLocalizedUserList()
}
//Click on User Cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Click")
}
//Localized User List (Collection View)
private func setUpLocalizedUserList(){
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
layout.minimumLineSpacing = 0
layout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 80)
let userListFrame = CGRect(x: 0, y: 0, width: Int(UIScreen.main.bounds.width), height: Int(UIScreen.main.bounds.height) - 160)
let myCollectionView:UICollectionView = UICollectionView(frame: userListFrame, collectionViewLayout: layout)
myCollectionView.dataSource = self
myCollectionView.delegate = self
myCollectionView.register(LocalizedUserCell.self, forCellWithReuseIdentifier: "userCell")
myCollectionView.register(GroupChatCell.self, forCellWithReuseIdentifier: "groupCell")
myCollectionView.backgroundColor = UIColor.white
addSubview(myCollectionView)
}
//Number of Section in User List
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
//Lists' Sizes
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (section == 0) ? 5 : 4
}
//Cells content here
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! LocalizedUserCell
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "groupCell", for: indexPath) as! GroupChatCell
return cell
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Maybe it's important - my users cells are inside another cell.
You can't make a segue from inSide collectionViewCell as present func needs a UIViewController , see here my answer for a table cell , the same logic TableCellNavigate
I'm putting the collection view into table view cell and I made it can display on the cell but when I want to select the collection cell (to change the color or print cell number), the select function is not working, I need to tap the cell many time to make it selected. Why the cell slow detect the selected item? What code will affect the cell to be selected?
This is the code for select the collection cell
override func awakeFromNib() {
super.awakeFromNib()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width
layout.scrollDirection = .vertical
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: width/5, height: width/4)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView?.collectionViewLayout = layout
collectionView?.delaysContentTouches = false
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
cell.cateImg.image = imageName[indexPath.row]
cell.cateLabel.text! = nameArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CategoryCollectionViewCell {
cell.cateImg.image = imageName2[indexPath.row]
print("collectionViewCell selected \(indexPath)")
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CategoryCollectionViewCell {
cell.cateImg.image = imageName[indexPath.row]
}
}
Project zip link:
https://www.dropbox.com/s/y10dgp3q61pi5n1/Finnciti.zip?dl=0
problem on AddViewCell.swift
Try to deselect this checkmark in Collection View properties
I fixed the problem after delete this code on AddViewController.
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(AddExpenseVC.dismissKeyboard))
view.addGestureRecognizer(tap)
#objc func dismissKeyboard() {
view.endEditing(true)
}
In AddViewController you are adding gesture recognizer to the view, which make every user gesture to be detected by the tap recognizer. You can remove this gesture:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(AddViewController.dismissKeyboard))
For dismissing keyboard you can implement tebleView delegate method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dismissKeyboard()
}