UIButton affecting UICollectionViewCell Appearance - ios

Ok, I have a ViewController that contains a UICollectionView and a UIButton. The Button is Not inside the collection view. When a user taps on an item in the UICollectionView I have some code that changes the background color of a UIView in the cell to show that it is selected. I have similar code for the UIButton which changes its background color when a user taps it, so it is acting like a styled checkbox.
The issue I am seeing is when I tap an item in the collection view, it highlights as it should. But then if I tap on the button, the button highlights normally but the collection view changes which item is highlighted. Even though the button that was tapped is not inside the collection view and has no ties to the collection view.
See following images:
first image: I have tapped on an item in the collection view, see the item is highlighted as normal.
second image: Notice, here, when I tap the Im not sure button, the collectionview changes and the highlight goes to a different cell. But no code in the DidSelectCellForItemAt is running
import Foundation
class SchedulingVisitForViewController: SchedulingViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var continueButton: RoundedButton!
#IBOutlet weak var notSureButton: RoundedButton!
#IBOutlet weak var notSureButtonTop: NSLayoutConstraint!
var collectionData = [[String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
self.appointment = fetchAppointment()
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionData.append([
"image": "regularExam",
"text": "Regular Exam",
"data": "regular_exam"])
self.collectionData.append([
"image": "vaccines",
"text": "Essential Vaccines",
"data": "vaccines"])
self.collectionData.append([
"image": "meds",
"text": "Parasite Meds",
"data": "preventitive_meds"])
self.collectionData.append([
"image": "dog",
"text": "My pet is sick",
"data": "sick_pet"])
self.notSureButton.layer.shadowColor = Constants.appColor.gray.light.cgColor
self.notSureButton.layer.shadowOffset = CGSize(width: 0, height: 2.0)
self.notSureButton.layer.shadowRadius = 3.0
self.notSureButton.layer.shadowOpacity = 0.8
self.notSureButton.layer.masksToBounds = false
}
override func viewDidLayoutSubviews(){
self.collectionView.frame = CGRect(x: self.collectionView.frame.origin.x, y: self.collectionView.frame.origin.y, width: self.collectionView.frame.size.width, height: CGFloat(2 * 170))
self.notSureButtonTop.constant = CGFloat(2 * 170) + 20.0
self.view.layoutIfNeeded()
self.collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.collectionData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SelectVisitForCollectionViewCell", for: indexPath) as! SelectVisitForCollectionViewCell
let item = indexPath.item
cell.displayContent(image: UIImage(named: self.collectionData[item]["image"]!)!, text: self.collectionData[item]["text"]!)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? SelectVisitForCollectionViewCell {
var categories = self.appointment?.categories
if let index = categories!.index(of: self.collectionData[indexPath.item]["data"]!) {
categories?.remove(at: index)
cell.showSelected(false)
} else {
categories?.append(self.collectionData[indexPath.item]["data"]!)
cell.showSelected(true)
}
self.appointment?.categories = categories!
if (self.appointment?.categories.count)! > 0 {
self.continueButton.isHidden = false
} else {
self.continueButton.isHidden = true
}
saveAppointment(data: self.appointment!)
}
}
#IBAction func onNotSureButtonPressed(_ sender: Any) {
var categories = self.appointment?.categories
if let index = categories!.index(of: "not_sure") {
categories?.remove(at: index)
self.notSureButton.backgroundColor = UIColor.white
self.notSureButton.setTitleColor(Constants.appColor.gray.dark, for: .normal)
} else {
categories?.append("not_sure")
self.notSureButton.backgroundColor = Constants.appColor.yellow.main
self.notSureButton.setTitleColor(UIColor.white, for: .normal)
}
self.appointment?.categories = categories!
if (self.appointment?.categories.count)! > 0 {
self.continueButton.isHidden = false
} else {
self.continueButton.isHidden = true
}
print(self.appointment?.categories)
saveAppointment(data: self.appointment!)
}
#IBAction func onContinueButtonPressed(_ sender: Any) {
self.parentPageboy?.scrollToPage(.next, animated: true)
}
}

The issue pointed out by caninster_exister is that I was reloading the collection view

Related

CollectionView reloadData() weird behaviour

I want to make a Collection View with cells, where you can select the colors of my little game. Therefor you can choose between different themes (each in one cell). If the theme is unlocked (by achieving scores), the blur view and the lock-icon should disappear, and all available cells/ themes should be selectable. I implemented this like so:
(Collection ViewController Class:)
import UIKit
import PMSuperButton
class myCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var backBtn: UIButton!
let items = ["Default dot", "Trap your first dot", "Score 15 or less (classic)", "Score 5 or less (classic)", "Score 15 or less (hard)", "Score 25 or less (no chill)", "Score 15 or less (snake)", "Score 15 or less everywhere", "Score 10 or less everywhere", "Circle the dot 100 times", "10"]
var preSelected = IndexPath()
#IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
updateUnlockedString()
updateColor()
}
func updateColor(){
view.backgroundColor = myColors().selected(value: selectedInstance.selectedColor).backgroundColor
}
func updateUnlockedString(){
let easyHS = UserDefaults.standard.integer(forKey: "highscoreeasy")
let classicHS = UserDefaults.standard.integer(forKey: "highscoreclassic")
let hardHS = UserDefaults.standard.integer(forKey: "highscorehard")
let nochillHS = UserDefaults.standard.integer(forKey: "highscoreno chill")
let snakeHS = UserDefaults.standard.integer(forKey: "highscoresnake")
var unlockedString = ""
//unimportant steps deleted
UserDefaults.standard.set(unlockedString, forKey: "unlocked")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! myCollectionViewCell
if(unlockInstance.unlockedString.contains(String(indexPath.item))){
cell.unlockedEffect()
print("unlocked Effect")
}
cell.label.text = items[indexPath.item]
cell.removeHighlight()
if indexPath.item == selectedInstance.selectedColor{
cell.highlightEffect()
preSelected = IndexPath(item: indexPath.item, section: indexPath.section)
}
cell.setupCellFilling(playerColor: myColors().selected(value: indexPath.item).playerColor, objColor: myColors().selected(value: indexPath.item).objColor, defaultColor: myColors().selected(value: indexPath.item).defaultColor, backgroundColor: myColors().selected(value: indexPath.item).backgroundColor)
myCollectionView = collectionView
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.allowsMultipleSelection = false
if ((collectionView.cellForItem(at: indexPath) as! myCollectionViewCell?) != nil) && unlockInstance.unlockedString.contains(String(indexPath.item)){
selectedInstance.selectedColor = indexPath.item
updateColor()
collectionView.reloadData()
}
}
#IBAction func backPressed(_ sender: Any) {
cameInstance.came = true
}
}
(Collection CELL Class:)
import UIKit
class myCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var view: UIView!
#IBOutlet weak var blurView: UIVisualEffectView!
#IBOutlet weak var lockedBlur: UIVisualEffectView!
#IBOutlet weak var lockedIcon: UIImageView!
override func layoutSubviews() {
// cell rounded section
self.layer.cornerRadius = 15.0
self.layer.masksToBounds = true
// cell shadow section
self.contentView.layer.cornerRadius = 15.0
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = myColors().selected(value: selectedInstance.selectedColor).defaultColor.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.layer.shadowRadius = 6.0
self.layer.shadowOpacity = 0.4
self.layer.cornerRadius = 15.0
self.layer.masksToBounds = false
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
super.layoutSubviews()
}
extension myCollectionViewCell{
func highlightEffect(){
self.layer.borderWidth = 5.0
self.layer.borderColor = UIColor.systemGreen.cgColor
}
func removeHighlight(){
self.layer.borderColor = UIColor.clear.cgColor
}
func unlockedEffect(){
lockedBlur.alpha = 0
lockedIcon.alpha = 0
}
func setupCellFilling(playerColor: UIColor, objColor: UIColor, defaultColor: UIColor, backgroundColor: UIColor){
lockedBlur.effect = UIBlurEffect(style: .extraLight)
blurView.effect = UIBlurEffect(style: .systemUltraThinMaterialDark)
lockedIcon.tintColor = objColor
view.backgroundColor = backgroundColor
}
}
The problem
When I open the CollectionView (viewDidLoad), everything appears 100% fine! All cells that should be locked, appear locked (lockedBlur.alpha = 0 and lockedIcon.alpha = 0). Also, only the locked cells are selectable, which I can see with the green border. Only the selected Cell has a green border, thats right.
BUT: When I select a cell, some random locked cells loose their blur view and loose their lock-icon (so blockedBlur and lockedIcon disappear). They still aren't selectable. Sometimes this affects all cells, sometimes only e.g. 2.
When I select a cell, the didSelectItemAt Function gets called. And this also calls updateColors() which only changes the background and calls the mysterious reloadData(). In my opinion the reloadData() SOMEHOW makes the lockedBlur and lockedIcon disappear, and I cannot find why....
Maybe someone can help me..
Thank you!!
SwiftHobby
I am working on Fitness App which has pretty similar working CollectionView behavior for it's training plan section. Basically you select certain cell when you finish an exercise. So I played around a lot with way of implementing selecting and deselecting cells. As I see you are missing selecting and deselecting functions of CollectionViewDelegate. There you set different states Effects
Here is the functions you have to use:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell {
cell.showIcon()
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell {
cell.hideIcon()
}
}

How do I make image visible when collection view cell is double tapped?

I have a collection view with a lot of images and all of these images has a heart image in the right corner. This heart image needs to be set to visible when the big image is double tapped as an indicator that it has been liked.
I have added a double tap gesture to my collection view and now I need to set the heart image to visible when this gesture happens on the selected cell.
Any suggestions to how I do it? I can't find any answers to this anywhere.
This is my collection view controller:
import UIKit
class OevelserCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
// MARK: - Properties
#IBOutlet weak var OevelserCollectionView: UICollectionView!
#IBOutlet var tap: UITapGestureRecognizer!
var oevelseCollectionViewFlowLayout: UICollectionViewFlowLayout!
let oevelseArray = OevelseArray()
// MARK: - Init
override func viewDidLoad() {
super.viewDidLoad()
setupOevelseCollectionView()
}
// MARK: - Functions
#IBAction func didDoubleTap(_ sender: UITapGestureRecognizer) {
print("tapped")
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
setupOevelseCollectionViewItemSize()
}
private func setupOevelseCollectionView() {
self.OevelserCollectionView.delegate = self
self.OevelserCollectionView.dataSource = self
let nib = UINib(nibName: "OevelseCollectionViewCell", bundle: nil)
OevelserCollectionView.register(nib, forCellWithReuseIdentifier: "OevelseCollectionViewCell")
}
private func setupOevelseCollectionViewItemSize() {
if oevelseCollectionViewFlowLayout == nil {
let numberOfItemPerRow: CGFloat = 1
let lineSpacing: CGFloat = 20
let interItemSpacing: CGFloat = 8
let width = (OevelserCollectionView.frame.width - (numberOfItemPerRow - 1) * interItemSpacing) / numberOfItemPerRow
let height = width - 50
oevelseCollectionViewFlowLayout = UICollectionViewFlowLayout()
oevelseCollectionViewFlowLayout.itemSize = CGSize(width: width, height: height)
oevelseCollectionViewFlowLayout.sectionInset = UIEdgeInsets.zero
oevelseCollectionViewFlowLayout.scrollDirection = .vertical
oevelseCollectionViewFlowLayout.minimumLineSpacing = lineSpacing
oevelseCollectionViewFlowLayout.minimumInteritemSpacing = interItemSpacing
OevelserCollectionView.setCollectionViewLayout(oevelseCollectionViewFlowLayout, animated: true)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return oevelseArray.oevelser.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OevelseCollectionViewCell", for: indexPath) as! OevelseCollectionViewCell
let oevelseText = oevelseArray.oevelser[indexPath.item].oevelseName
let oevelseImage = oevelseArray.oevelser[indexPath.item].oevelseImage
cell.oevelseLabel.text = oevelseText
cell.oevelseImageView.image = UIImage(named: oevelseImage)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
And here is my collection view cell class:
import UIKit
class OevelseCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var oevelseImageView: UIImageView!
#IBOutlet weak var oevelseLabel: UILabel!
#IBOutlet weak var isLikedImageView: UIImageView!
#IBOutlet weak var heartImageWidthConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
}
Inside cellForRowAt
cell.oevelseImageView.image = UIImage(named: oevelseImage)
cell.oevelseImageView.tag = indexPath.row
let tapGR = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapGR.numberOfTapsRequired = 2
cell.oevelseImageView.addGestureRecognizer(tapGR)
#objc func handleTap(_ gesture: UITapGestureRecognizer){
let index = gesture.view!.tag
guard let cell = tableView.cellForRow(at:IndexPath(row:index,section:0)) else { return }
arr[index].isLiked.toggle()
cell.isLikedImageView.image = arr[index].isLiked ? <#likeImg#> : <#defImg#>
}
OR
#objc func handleTap(_ gesture: UITapGestureRecognizer){
arr[gesture.view!.tag ].isLiked.toggle()
self.tableView.reloadRows(at:[IndexPath(row:index,section:0)],with:.none)
}

UICollectionView showing unloaded xib and not updating the content inside using swift

I have a collection view which is loaded from a .xib file. When the view opens sometimes the collection view will have loaded the content and other times it does not load any content into the cell causing just the .xib to be shown. Other times the .xib doesn't even show either. However, I don't understand why this is happening. When clicking on the cell, a new viewController opens with a detailed view which has the content loaded so the cell obviously knows what is suppose to be shown.
var currentUser: User!
var listCategories: [String] = ["Friends Lists", "Friends", "People"]
var lists = [Media]()
in viewDidLoad:
collectionView.register(UINib(nibName: "ListCell2.0", bundle: nil), forCellWithReuseIdentifier: Storyboard.listCell)
collectionView.reloadData()
observeMedia()
observeMedia():
func observeMedia() {
Media.observeNewMedia { (media) in
if !self.lists.contains(media) {
self.lists.insert(media, at: 0)
self.collectionView.reloadData()
}
}
}
viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
observeMedia()
}
collectionView Methods:
extension HomeViewController
{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return listCategories.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return lists.count
}else{
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Storyboard.listCell, for: indexPath) as! ListCell
cell.layer.applySketchShadow(color: UIColor.black, alpha: 0.08, x: 0, y: 0, blur: 10, spread: 0)
cell.layer.cornerRadius = 20
cell.layer.masksToBounds = false
cell.currentUser = self.currentUser
cell.media = self.lists[indexPath.item]
cell.mainView.setGradientBackground(colours: self.getColourFromTag(tag: self.lists[indexPath.item].tag))
return cell
}
//section header view
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
let sectionHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: Storyboard.sectionHeader, for: indexPath) as! SectionHeaderView
let category = listCategories[indexPath.section]
sectionHeaderView.sectionTitle = category
return sectionHeaderView
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: Storyboard.showListDetailSegue, sender: nil)
}
the CollectionView Cell
import UIKit
import Foundation
import SAMCache
class ListCell: UICollectionViewCell {
#IBOutlet weak var nameView: UIView!
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var nameButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
//#IBOutlet weak var tagLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var listTitle: UILabel!
#IBOutlet weak var boughtLabel: UILabel!
#IBOutlet weak var boughtProgress: UIProgressView!
var numOfItems = 0
var numOfBought = 0
var counter: Double = 0{
didSet{
boughtProgress.isHidden = false
let fractionalProgress = Float(counter)
boughtProgress.setProgress(fractionalProgress, animated: true)
}
}
var currentUser: User!
var media: Media! {
didSet{
if currentUser != nil{
self.updateUI()
}
}
}
var cache = SAMCache.shared()
func updateUI(){
let profileImageKey = "\(media.createdBy.uid)-profileImage"
if let image = cache?.object(forKey: profileImageKey) as? UIImage {
self.profileImageView.image = image
}else{
media.createdBy.downloadProfilePicture { [weak self] (image, error) in
if let image = image {
self?.profileImageView.image = image
self?.cache?.setObject(image, forKey: profileImageKey)
}else if error != nil {
print(error)
}
}
}
mainView.layer.cornerRadius = 20
mainView.layer.masksToBounds = true
//profile image
profileImageView.layer.cornerRadius = profileImageView.bounds.height / 2.0
profileImageView.layer.masksToBounds = true
//name
nameButton.setTitle("\(media.createdBy.firstName) \(media.createdBy.lastName)", for: [])
nameView.layer.cornerRadius = 20
nameView.layer.masksToBounds = true
//date
dateLabel.text = "\(convertDateFormatter(theDate: media.dueAt))"
dateLabel.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.3)
dateLabel.textColor = UIColor.white
dateLabel.layer.cornerRadius = dateLabel.bounds.height / 2.0
dateLabel.layer.masksToBounds = true
//title
listTitle.text = "\(media.title)"
//progress
numOfItems = media.items.count
print("num of items \(media.items.count)")
counter = Double(numOfBought)/Double(numOfItems)
boughtLabel.text = "\(numOfBought)/\(numOfItems) Bought"
boughtProgress.layer.cornerRadius = boughtProgress.bounds.height / 2.0
boughtProgress.layer.masksToBounds = true
}
#IBAction func arrowDidTap(){
print("arrow tapped")
print(media.tag)
}
func convertDateFormatter(theDate: String) -> String
{
print(theDate)
let newFormat = DateFormatter()
newFormat.dateFormat = "dd/MM/yyyy"
let dueDate = newFormat.date(from: theDate)
newFormat.dateFormat = "dd MMM yy"
print(newFormat.string(from: dueDate!))
return newFormat.string(from: dueDate!)
}
The first image shows when the view first loads. this is just what is shown in the .xib, however, the gradient has loaded, not the content
the second image shows how it should look. This is after scrolling through the view

UICollectionView Reloading cells backwards

I am using a UICollectionView as a square grid layout for a crossword. The user will 'create' the puzzle by first tapping each cell to change the colour, press the 'insert numbers button' then add the numbers for each square and finally press the 'get clues button'. When the 'insert numbers button' is pressed, the cells are reloaded to 'fix' the colour and a textfield for each square's number is unhidden (for white cells only). The problem arises when I reload the cells to make the square's number un-editable, the cells reload in reverse. I.e. if the first cell (index 0) has a number, the last cell is updated (index 63). Pressing the button twice puts the cells in the correct position.
I have printed the indexPath to see which cell has the text and it is always the reverse...
Code:
import UIKit
class gridSetup: UIViewController, UICollectionViewDataSource,
UICollectionViewDelegate {
var setColours: Bool = true
var setNumbers: Bool = false
var numbers: Array = [Int]()
var whiteSquares:Array = [IndexPath]()
var gridSizer: Int? //value passed in from previous screen
#IBOutlet weak var getCluesButton: UIButton!
#IBOutlet weak var insertNumbersButton: UIButton!
#IBOutlet weak var grid: UICollectionView!
let reuseIdentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
setColours = true
setNumbers = false
getCluesButton.isHidden = true
self.navigationController?.isNavigationBarHidden = false
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//return (self.gridSizer * self.gridSizer)
return (self.gridSizer! * self.gridSizer!)
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
cell.backgroundColor = UIColor.white
if setColours == true {
whiteSquares.append(indexPath) //initalise whiteSquares by adding all the squares
cell.cellGuess.isHidden = true //hide the letter guesses
cell.squareNumber.isHidden = true //hide the sqaure numbers
}
//redraw cells to fix the colours
// if we are on a white cell and are done setting colours
if setColours == false && whiteSquares.contains(indexPath) == true {
if setNumbers == true {
cell.squareNumber.isHidden = false
}
else {
if cell.squareNumber.text != "" {
print("I have a number inside, Index: ", indexPath.row)
}
cell.squareNumber.isUserInteractionEnabled = false
cell.squareNumber.placeholder = ""
cell.cellGuess.isHidden = false
return cell
}
}
// on a black square
else if setColours == false && whiteSquares.contains(indexPath) == false {
cell.squareNumber.isHidden = true
//print("on a black square", indexPath)
cell.backgroundColor = UIColor.black
cell.squareNumber.isHidden = true
cell.cellGuess.isHidden = true
}
//initial grid setup
else {
cell.backgroundColor = UIColor.white
cell.layer.borderColor = UIColor.black.cgColor
cell.squareNumber.isHidden = true
}
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
//print("You selected cell #\(indexPath.item)!")
}
// change background color back when user releases touch
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
if setColours == true {
if cell?.backgroundColor == UIColor.black {
//print("add", indexPath.row)
cell?.backgroundColor = UIColor.white
var k: Int = 0
for i in whiteSquares {
if indexPath.row < i.row {
whiteSquares.insert(indexPath, at: k)
break
}
else {
k += 1
}
}
}
else {
//print("remove", indexPath.row)
cell?.backgroundColor = UIColor.black
var k: Int = 0
for i in whiteSquares {
if indexPath.row == i.row {
whiteSquares.remove(at: k)
}
else {
k += 1
}
}
}
}
}
#IBAction func getClues(_ sender: Any) {
setNumbers = false
grid.reloadData() //problem comes from/after calling this function
}
#IBAction func insertNumbers(_ sender: Any) {
setColours = false
insertNumbersButton.isHidden = true
setNumbers = true
getCluesButton.isHidden = false
grid.reloadData()
}
}
// some layout stuff, (not important)
extension gridSetup: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width / (CGFloat(gridSizer! + 1))
//let width = collectionView.frame.size.width / (CGFloat(gridSizer! + 1))
let height = width
return CGSize(width: width, height: height)
}
}
The '1' should be in the 1st row, second column

How to access buttons in a UICollectionView from a target function set (Swift 3)

In my View Controller, I have a collection view that, when rendered, displays 3 cells, each of which has a label, and a button. The label displays the name of a color, and the button has a background image that displays a color swatch.
I want it so that whenever you click on one of the buttons, that button gets a dark border around it, while the other buttons get a light border on them, to indicate the clicked-on button as being "selected". Alternately, I could probably do this by changing the image out based on the selected state of the image - but my question remains the same.
How do I access the other two buttons, to toggle their properties?
I have a script implemented that allows me to add a border to the button that somebody clicked on - but I cannot figure out how to access the other buttons, in the other cells of the CollectionView to alter their border properties as well.
Here is my source code (with irrelevant/unrelated bits stripped out)
class trimSelectorVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var trimSelector: UICollectionView!
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
}
var trimArray: [trimObject] = []
override func viewDidLoad() {
super.viewDidLoad()
trimArray.append(trimObject(trimName: "Chrome", trimButton: "chrome-swatch", trimID: 0))
trimArray.append(trimObject(trimName: "Gold", trimButton: "gold-swatch", trimID: 1))
trimArray.append(trimObject(trimName: "Gun Metal", trimButton: "gunmetal-swatch", trimID: 2))
trimSelector.delegate = self
trimSelector.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trimArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
//Set the label text
cell.trimLabel.text = trimArray[indexPath.item].trimName
//Set the image for the button
cell.trimButton.setImage(UIImage(named: trimArray[indexPath.item].trimButton), for: UIControlState.normal)
//Sets a target function for the button
cell.trimButton.addTarget(self, action: #selector(selectedSwatch), for: .touchUpInside)
return cell
}
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
sender.layer.borderWidth = 2
sender.layer.borderColor = UIColor(red: 83/255, green: 71/255, blue: 65/255, alpha: 1.00).cgColor
}
}
Can anybody please tell me how to access the other buttons in my "selectedSwatch" function?
There are various ways you can handle this. A UICollectionView view has a method visibleCells() that returns an array of it's visible cells. You could use that to get pointers to your cells. You would need a way to figure out which one is which. You could use indexPath(for: UICollectionViewCell) to figure out the index path of each cell, for example.
I don't know if this might help, but what about if you store the IndexPath on your struct on cellForItemAt method?
You will have:
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
var idx : IndexPath
}
Then on:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
....
trimArray[indexPath.item].idx = indexPath
....
}
And in your selectedSwatch method:
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
sender.layer.borderWidth = 2
sender.layer.borderColor = UIColor(red: 83/255, green: 71/255, blue: 65/255, alpha: 1.00).cgColor
if let cell = (sender.superview as? UICollectionViewCell) {
//Cell with the button selected:
let idx = collectionView.indexPath(for: cell)
//array of the other objects:
let allOtherObjects = trimArray.filter { ($0 as! trimObject).idx != idx }
allOtherObject.forEach({ (trimObj) in
let cell = collection.cellForItem(at: trimObj.idx)
//Do whatever yo need to do...
//cell.trimButton.layer
})
}
}
Its may be to late but still useful for somebody
Swift 4 version:
You can use sender superview as UiCollectionViewCell
* Consider hierarchy of sender in collection view cell
func selectedSwatch(sender: UIButton) {
let cell = sender.superview?.superview as! trimSelectionCell
//cell.yourbtn
}
Try this,
class trimSelectorVC: UIViewController, UICollectionViewDelegate,
UICollectionViewDataSource {
#IBOutlet weak var trimSelector: UICollectionView!
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
var isSelected : String
}
var trimArray: [trimObject] = []
override func viewDidLoad() {
super.viewDidLoad()
trimArray.append(trimObject(trimName: "Chrome", trimButton: "chrome-swatch", trimID: 0,isSelected : "0"))
trimArray.append(trimObject(trimName: "Gold", trimButton: "gold-swatch", trimID: 1,isSelected : "0"))
trimArray.append(trimObject(trimName: "Gun Metal", trimButton: "gunmetal-swatch", trimID: 2,isSelected : "0"))
trimSelector.delegate = self
trimSelector.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trimArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
//Set the label text
cell.trimLabel.text = trimArray[indexPath.item].trimName
//Set the image for the button
cell.trimButton.setImage(UIImage(named:
trimArray[indexPath.item].trimButton), for: UIControlState.normal)
if(trimArray[indexPath.item].isSelected == "0"){
// button not clicked
// change shadow color of button
}
else
{
// button clicked
cell.trimButton.layer.borderWidth = 2
cell.trimButton.layer.borderColor = UIColor(red: 83/255, green:
71/255,blue: 65/255, alpha: 1.00).cgColor
}
// set tag to the button
cell.trimButton.tag = indexPath.item
//Sets a target function for the button
cell.trimButton.addTarget(self, action:#selector(selectedSwatch),
for: .touchUpInside)
return cell
}
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
let index = sender.tag
for obj in trimArray {
obj.isSelected = "0"
}
trimArray[index].isSelected = "1"
collectionView.reloadData()
}
}

Resources