Swift: Segmented control behaves in a weird way in UITableView Cell - ios

Anytime I tap segmented control in UICell, immediately some other cell gets this segmented control in the same position. It looks like segmented control recognizes that not only this particular one was tapped but also some other one in other cell.
Have you ever encountered issue like this?
this is my custom cell implementation:
class QuestionYesNoCustomCellTableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var segmentControl: ADVSegmentedControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
segmentControl.items = ["TAK", "NIE"]
segmentControl.font = UIFont(name: "Avenir-Black", size: 12)
segmentControl.borderColor = UIColor.grayColor()
segmentControl.selectedIndex = 1
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.addTarget(self, action: "segmentValueChanged:", forControlEvents: .ValueChanged)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func segmentValueChanged(sender: AnyObject?){
if segmentControl.selectedIndex == 0 {
segmentControl.thumbColor = UIColor(red: 231.0/255.0, green: 76.0/255.0, blue: 60.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
}else if segmentControl.selectedIndex == 1{
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.grayColor()
segmentControl.unselectedLabelColor = UIColor.whiteColor()
}
}
Also, I think it is worth to provide my tableView delegate methods implemented
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (dict2 as NSDictionary).objectForKey(dictKeysSorted[section])!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: QuestionYesNoCustomCellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! QuestionYesNoCustomCellTableViewCell
cell.questionLabel.text = (dict2 as NSDictionary).objectForKey(dictKeysSorted[indexPath.section])![indexPath.row] as? String
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor(red: 245.0/255.0, green: 245.0/255.0, blue: 245.0/255.0, alpha: 1.0)
}
else {
cell.backgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 0.7)
}
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dictKeysSorted[section]
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("CellHeader") as! CustomHeaderCell
headerCell.backgroundColor = UIColor(red: 20.0/255.0, green: 159.0/255.0, blue: 198.0/255.0, alpha: 1.0)
headerCell.headerLabel.text = dictKeysSorted[section]
return headerCell
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return dictKeysSorted.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 110.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
To recap what the problem actually is: In every tableView cell there is a segment control. When I change the position of the one located in first row, I scroll down and see that segment control in row 5 also has been moved despite the fact it should be in the default position.
Thanks in advance
EDIT:
I recognized one of the biggest problem in solutions below - they are good as long as you don't use section in tableView. The thing is, from what I have discovered right now, in each sections the rows are counted over from 0.

This might be the cause when you are using reusing the cells, when you scroll the cell you changed will be shown again for another row.
To avoid this when you reuse cell make sure you reset the data in it also
In your case you have to check if the segmented value is changed then change the segmented control value also in cellForRowAtIndexPath
Please let me know if you need more explanation.
Here is a sample project for you sampleTableReuse

It's because of reusable nature of UITableViewCells. You must keep track in your datasource selected segment index for each row. Then in cellForRowAtIndexPath you must set it properly for each cell.
example
define somewhere an enum with possible Answers:
enum Answer {
case Yes
case No
case None
}
then define and init your answers' array:
var answer = [Answer](count: numberOfQuestions, repeatedValue: .None)
in your cell's implementation add a method to configure a cell with Answer
func setupWithAnswer(answer: Answer)
{
var selectedIdex = UISegmentedControlNoSegment
switch answer {
case .Yes: selectedIdex = 0
case .No: selectedIdex = 1
default: break
}
self.segmentedControl.selectedSegmentIndex = selectedIdex
}
and finally, in your cellForRowAtIndex do after dequeuing
cell.setupWithAnswer(answer: self.answers[indexPath.row])

Related

Making custom UITableView Cells Display Contents Properly (Not Blank)

I have been working on making an iOS app which requires a screen/view that is scrollable and has an image, then a list and then an image and then another image (attached is the screenshot from the Android version I made)
Top of the view
View Scrolled
I have tried using the following code, which gives me the correct amount of cells but they are all blank.
//
// ServicesTableViewController.swift
// Contact Australis
//
// Created by Raghav Khanna on 22/4/18.
// Copyright © 2018 Australis. All rights reserved.
//
import UIKit
class ServiceViewCell: UITableViewCell {
#IBOutlet weak var IMage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
IMage.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServiceViewCellList: UITableViewCell {
#IBOutlet weak var somethin_label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let color = UIColor(red: 0/255, green: 105/255, blue: 191/255, alpha: 1.0).cgColor
let back_colour = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0).cgColor
let back_colour_ui = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0)
let radius: CGFloat = 5
let border_width:CGFloat = 1.5
somethin_label.layer.borderColor = color
somethin_label.layer.borderWidth = border_width
somethin_label.layer.cornerRadius = radius
somethin_label.backgroundColor = back_colour_ui
}
var items_maintenance = ["Painting","All Lighting & Globe Replacemt",
"Carpet & Hard Floor Replacement","Electrical Work & Maintenance","Plumbing Work & Maintenance","Test & Tag Completion","Office Furniture Removal", "Hard Waste Removal", "Window Frosting", "All Other Handy Man & Maintenance Tasks"]
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
} class ServiceViewCellCleaning: UITableViewCell {
#IBOutlet weak var Title: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
Title.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServiceViewCellCleaningList: UITableViewCell {
#IBOutlet weak var other_label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let color = UIColor(red: 0/255, green: 105/255, blue: 191/255, alpha: 1.0).cgColor
let back_colour = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0).cgColor
let back_colour_ui = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0)
let radius: CGFloat = 5
let border_width:CGFloat = 1.5
other_label.layer.borderColor = color
other_label.layer.borderWidth = border_width
other_label.layer.cornerRadius = radius
other_label.backgroundColor = back_colour_ui
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServicesTableViewController: UITableViewController {
let basicCellIdentifier = "BasicCell"
var items_maintenance = ["Painting","All Lighting & Globe Replacement", "Carpet & Hard Floor Replacement","Electrical Work & Maintenance","Plumbing Work & Maintenance","Test & Tag Completion","Office Furniture Removal", "Hard Waste Removal", "Window Frosting", "All Other Handy Man & Maintenance Tasks"]
var items_cleaning = ["All Genral Comercial Cleaning","Office Cleaning", "Initial Clean","Spring Clean","Steam Carpet Cleaning","Window Washing","High Pressure Washing", "Waste Removal", "Strip & Seal Hard Floors", "Scrubbing & Buffing Hard Floors"]
let cellSpacingHeight: CGFloat = 5
#IBOutlet var table: UITableView!
func configureTableView() {
//tableView.rowHeight = UITableViewAutomaticDimension
//tableView.estimatedRowHeight = 1000.0
//let rect = CGRect(origin: .zero, size: CGSize(width: 400, height: 400))
//self.tableView = UITableView(frame: rect, style: UITableViewStyle.plain)
table.register(ServiceViewCell.self, forCellReuseIdentifier: "maintenance")
table.register(ServiceViewCellList.self, forCellReuseIdentifier: "customcell")
table.register(ServiceViewCellCleaning.self, forCellReuseIdentifier: "cleaning")
table.register(ServiceViewCellCleaningList.self, forCellReuseIdentifier: "cleaning_customcell")
}
/*func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return
}*/
override func viewDidLoad() {
super.viewDidLoad()
self.configureTableView()
table.reloadData()
table.delegate = self
table.dataSource = self
// Uncomment the following line to preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 4
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else if section == 1 {
return items_maintenance.count
} else if section == 2 {
return 1
}
else {
return items_cleaning.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let maintenance_title = table.dequeueReusableCell(withIdentifier: "maintenance", for: indexPath) as! ServiceViewCell
let maintenance_list = table.dequeueReusableCell(withIdentifier: "customcell", for: indexPath) as! ServiceViewCellList
let cleaning_title = table.dequeueReusableCell(withIdentifier: "cleaning", for: indexPath) as! ServiceViewCellCleaning
let cleaning_list = table.dequeueReusableCell(withIdentifier: "cleaning_customcell", for: indexPath) as! ServiceViewCellCleaningList
maintenance_list.somethin_label?.text = self.items_maintenance[indexPath.row]
maintenance_list.somethin_label?.adjustsFontSizeToFitWidth = false
maintenance_list.somethin_label?.font = UIFont.systemFont(ofSize: 10.0)
cleaning_list.other_label?.text = "test"
cleaning_list.other_label?.adjustsFontSizeToFitWidth = false
cleaning_list.other_label?.font = UIFont.systemFont(ofSize: 10.0)
cleaning_title.Title?.image = UIImage(named: "cleaning.png")
maintenance_title.IMage?.image = UIImage(named: "maintenance.png")
if indexPath.section == 0 {
return maintenance_title
} else if indexPath.section == 1 {
return maintenance_list
} else if indexPath.section == 2 {
return cleaning_title
}
else {
return cleaning_list
}
return cleaning_list
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
//Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return false
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
My Storyboard looks like this (Its another view in the main storyboard with for prototype cells with custom classes) and I am struggling to figure out why I keep getting either "Unexpectedly found nil while unwrapping an Optional value" for "maintenance_list.somethin_label!.text = self.items_maintenance[indexPath.row]" or this (blank cells) when I use '?' instead of '!'.
I know why I don't get the nil while unwrapping error when using the '?'. But the real problem is why I am not being able to interact with the views in each of the cells to display the desired data. I have checked all the outlets, and they are all correct.
Any help would be greatly appreciated.
Thanks in advance.
Without having access to the entire project is difficult to say why it's not working.
But I think the approach you are following is not correct, you should look into having only two sections (Maintenance, Cleaning) and then each item of Maintenance and Cleaning would be a cell, so your datasource should return 2 sections and 10 rows for each section.
You would need a section header, which would have an image view, and then only one prototype cell, that you can reuse for any row.
Hope this helps.

swift: How to set UITableViewCell display fullscreen with InfiniteScrolling (allow paging)

I have a custom UITableView with infinite Scroll and Paging Enable. Each of my cells has UIImageView on background, I want each time I scroll up or down it will display each image as full screen.
I used this function for full screen, but my view is not full screen after one time infinite scroll.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.view.frame.size.height
}
Late Answer, Might help to others.
For Full-screen Paging of your TableViewcell. Follow the below steps.
(Consider as Without navigation bar)
TableView constrain should be with superview (not safeArea) in allside with 0 constant.
configure your Tableview like below.
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
}
private func configureTableView() {
tableView.rowHeight = UIScreen.main.bounds.height
tableView.estimatedRowHeight = UIScreen.main.bounds.height
tableView.separatorStyle = .none
tableView.isPagingEnabled = true
tableView.bounces = false
tableView.estimatedSectionHeaderHeight = CGFloat.leastNormalMagnitude
tableView.sectionHeaderHeight = CGFloat.leastNormalMagnitude
tableView.estimatedSectionFooterHeight = CGFloat.leastNormalMagnitude
tableView.sectionFooterHeight = CGFloat.leastNormalMagnitude
tableView.contentInsetAdjustmentBehavior = .never
tableView.delegate = self
tableView.dataSource = self
}
Please find below source code in which you will get table view scrolling along with the different images.
Swift 4
//
// ImageTVC.swift
//
// Created by Test User on 06/02/18.
// Copyright © 2018. All rights reserved.
//
import UIKit
class ImageTVC: UITableViewCell {
#IBOutlet weak var imgView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
//
// TableViewVC.swift
//
// Created by Test User on 06/02/18.
// Copyright © 2018. All rights reserved.
//
class TableViewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension TableViewVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
//----------------------------------------------------------------
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! ImageTVC
if indexPath.item % 2 == 0 {
cell.imgView?.backgroundColor = UIColor(red: CGFloat(indexPath.item * 2)/255.0, green: CGFloat(indexPath.item * 0)/255.0, blue: CGFloat(indexPath.item * 0)/255.0, alpha: 1.0)
} else if indexPath.item % 3 == 0 {
cell.imgView?.backgroundColor = UIColor(red: CGFloat(indexPath.item * 0)/255.0, green: CGFloat(indexPath.item * 2)/255.0, blue: CGFloat(indexPath.item * 0)/255.0, alpha: 1.0)
} else {
cell.imgView?.backgroundColor = UIColor(red: CGFloat(indexPath.item * 0)/255.0, green: CGFloat(indexPath.item * 0)/255.0, blue: CGFloat(indexPath.item * 2)/255.0, alpha: 1.0)
}
print("IndexPath.row = \(indexPath.row)")
return cell
}
//----------------------------------------------------------------
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.bounds.size.height
}
}
Also find storyboard design.

How can I filter more than one thing out of return from API in Swift 3

I am including my code that I have implemented to filter through sortedDiscipline names.
The problem I am having --even though it is filtering through the names-- is after the IndexPath.row is changed from the filtering, the image is incorrect for the current game, and also the cell that is populated cannot be selected because the didSelectRow does not follow the broken IndexPath.
import UIKit
import Foundation
import Alamofire
import SwiftyJSON
import Firebase
import FirebaseDatabase
class AllGamesTableViewController: UITableViewController, UISearchResultsUpdating {
let urlFront = "https://www.toornament.com/disciplines/"
let urlImagePath = "/img/icon-48x48-medium.png"
var selectedRow: Int?
var newArray = [String]()
var filteredGames = [String]()
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController:nil)
searchController.dimsBackgroundDuringPresentation = true
searchController.searchBar.sizeToFit()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.searchBarStyle = UISearchBarStyle.minimal
// Include the search bar within the navigation bar.
navigationItem.titleView = self.searchController.searchBar
definesPresentationContext = true
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = #colorLiteral(red: 0.1137254902, green: 0.168627451, blue: 0.1764705882, alpha: 1)
self.view.backgroundColor = #colorLiteral(red: 0.1137254902, green: 0.168627451, blue: 0.1764705882, alpha: 1)
self.navigationItem.setHidesBackButton(true, animated: true)
// self.navigationItem.setHidesBackButton(true, animated: true)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (searchController?.isActive)!{
return filteredGames.count
}else{
return ApiManager.shared.sortedDisipline.count
}
}
let getID = ApiManager.shared.disciplinesID
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "allGamesCell", for: indexPath)
cell.backgroundColor = UIColor(red: 29.0/255.0, green: 43.0/255.0, blue: 45.0/255.0, alpha: 1)
cell.textLabel?.textColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
let disciplineid = ApiManager.shared.sortedDisciplineID[indexPath.row]
if (searchController?.isActive)!{
cell.textLabel?.text = filteredGames[indexPath.row]
cell.textLabel?.textColor = UIColor(red: 255.0/255.0, green: 165.0/255.0, blue: 0.0/255.0, alpha: 1)
cell.imageView?.image = UIImage(named: "\(disciplineid).png")
}else{
cell.textLabel?.textColor = UIColor(red: 255.0/255.0, green: 165.0/255.0, blue: 0.0/255.0, alpha: 1)
cell.textLabel?.text = ApiManager.shared.sortedDisipline[indexPath.row]
cell.imageView?.image = UIImage(named: "\(disciplineid).png")
}
return cell
}
func updateSearchResults(for searchController: UISearchController) {
filteredGames.removeAll(keepingCapacity: false)
//filter through the all games
filteredGames = ApiManager.shared.sortedDisipline.filter {
game in
game.lowercased().contains(searchController.searchBar.text!.lowercased())
}
// if searchController.searchBar.text != ""{
tableView.reloadData()
}
// }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if searchController.isActive && searchController.searchBar.text != "" {
print(indexPath.row)
let DisciplineID = filteredGames[indexPath.row]
TournamentStore.shared.currentDisciplineId = DisciplineID
performSegue(withIdentifier: "tournamentList", sender: self)
} else {
let DisciplineID = ApiManager.shared.sortedDisciplineID[indexPath.row]
print("\(DisciplineID) \("did on click")")
TournamentStore.shared.currentDisciplineId = DisciplineID
performSegue(withIdentifier: "tournamentList", sender: self)
}
}
Suggestions to solve the issues.
Do not use separate arrays for disciplinesID and sortedDisipline
Use the class or struct representing discipline for both the data source array and the filteredGames array.
Get the disciplineid always directly from the discipline object. (Solves the image issue)
In didSelectRowAt get the object depending on searchController?.isActive like in the other methods (solves the indexPath issue)
PS: Initialize the search controller lazily and non-optional. That avoids a lot of question and exclamation marks.
Migrated from my comment for more explanation.
The reason your selection is incorrect is in your didSelectRowAt indexPath if you are searching you are not using the filtered list to get the ID. This is also why the image is wrong when reloading, you need to get the ID out of the filtered array if sorting
Something like this:
let id: String
if filtering {
id = filteredGames[indexPath.row]
} else {
id = ApiManager.shared.sortedDisciplineID[indexPath.row])
}
then use that id for the image and for opening it. Essentially when you sort you are changing how the names match up with the row ID's.
Most of my problem was that
searchController.dimsBackgroundDuringPresentation was set to true....it needed to be false.

How can I highlight selected UICollectionView cells? (Swift)

I have a UICollectionView, and the user is able to select multiple cells. It's a bit difficult to keep track of which cells have been selected, so I need some way to go about highlighting/creating a border when the cell is tapped.
Code:
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
addToList.append(objectsArray[indexPath.row])
return true
}
you can use border change on didSelectItemAtIndexPath override event like the below code and assign new settings on the cell.
Swift 3.x:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
addToList.append(objectsArray[indexPath.row])
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.borderWidth = 2.0
cell?.layer.borderColor = UIColor.gray.cgColor
}
Here is my solution, and I'm sure it works.
My solution includes 3 highlight effects, UICollectionCell's selectedBackgroundView, cell.contentView.backgroundColor, or your your own specialHighlightedArea; just feel free to choose the one you need, and feel free to add more effects as your App's Designer requires.
How to use? Just inherit BaseCollectionViewCell. If needed, configure in cell's init or collectionView's delegate methods.
If you don't need highlight effect, just find a method named 'shouldHighlightItemAtIndexPath' in UICollectionViewDelegate and return false or just set cell.shouldTintBackgroundWhenSelected = false.
extension UIColor {
convenience init(rgb: Int, alpha: CGFloat = 1.0) {
self.init(red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgb & 0xFF00) >> 8) / 255.0, blue: CGFloat(rgb & 0xFF) / 255.0, alpha: alpha)
}
}
/// same with UITableViewCell's selected backgroundColor
private let cellHighlightedColor = UIColor(rgb: 0xD8D8D8)
class BaseCollectionViewCell: UICollectionViewCell {
var shouldTintBackgroundWhenSelected = true // You can change default value
var specialHighlightedArea: UIView?
// make lightgray background show immediately(on touch)
// (使灰背景在手指触到 cell 时立即出现)
override var isHighlighted: Bool {
willSet {
onSelected(newValue)
}
}
// keep lightGray background from selected until unselected
// (保留灰背景直至取消选中)
override var isSelected: Bool {
willSet {
onSelected(newValue)
}
}
func onSelected(_ newValue: Bool) {
// selectedBackgroundView is defined by UICollectionViewCell
guard selectedBackgroundView == nil else { return }
if shouldTintBackgroundWhenSelected {
contentView.backgroundColor = newValue ? cellHighlightedColor : UIColor.clear
}
if let sa = specialHighlightedArea {
sa.backgroundColor = newValue ? UIColor.black.withAlphaComponent(0.4) : UIColor.clear
}
}
}
SWIFT
Add this code to your UICollectionViewCell subclass:
override var isSelected: Bool {
didSet{
if self.isSelected {
UIView.animate(withDuration: 0.3) { // for animation effect
self.backgroundColor = UIColor(red: 115/255, green: 190/255, blue: 170/255, alpha: 1.0)
}
}
else {
UIView.animate(withDuration: 0.3) { // for animation effect
self.backgroundColor = UIColor(red: 60/255, green: 63/255, blue: 73/255, alpha: 1.0)
}
}
}
}
This will set the color of a single selected cell, and remove the selected color from any previous selected cells. I've added a smooth animation to it. I think it's nice, but it's optional.
Use
collectionView.reloadItemsAtIndexPaths([indexPath])
to reload current cell, or
collectionView.reloadData()
to reload all cells in shouldSelectItemAtIndexPath
Then in cellForItemAtIndexPath set your border or background color if the cell is marked as checked (you may need a new array for checked cells with preferably indexPaths.
You can create a customized collcetionViewCell, and override:
class MyCell: UICollectionViewCell {
override var isHighlighted: Bool {
didSet {
if self.isHighlighted {
print("yes")
// Your customized animation or add a overlay view
} else {
print("no")
// Your customized animation or remove overlay view
}
}
}
}
This way, you can create similar result like the highlight effect on UITableViewCell.
Without subclassing:
If you don't want to create your own collectionViewCell. you can use the delegate method:
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)
You can do the same thing with it.
For multiple selection of cell, you can do it as follow:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
if let currentCell = collectionView.cellForItem(at: indexPath) as? QuestionnaireCollectionViewCell {
// Your selection logic, you can change it according to your requirement
if currentCell.selectedImage.isHidden == true{
currentCell.selectedImage.isHidden = false
}
else{
currentCell.selectedImage.isHidden = true
}
}
}
}
For single selection you can use isSelected in your collectionviewcell class as follow:
override var isSelected: Bool{
didSet{
if self.isSelected
{
//This block will be executed whenever the cell’s selection state is set to true (i.e For the selected cell)
}
else
{
//This block will be executed whenever the cell’s selection state is set to false (i.e For the rest of the cells)
}
}
}
Try to make the borders thick enough to cover the entire cell
Code:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
addToList.append(objectsArray[indexPath.row])
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.borderWidth = 200.0
cell?.layer.borderColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.4).cgColor
}
Try this code to highlight collection view cell
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = #colorLiteral(red: 1, green: 0.4932718873, blue: 0.4739984274, alpha: 1)
}
}
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
With ternary operator
override var isSelected: Bool {
didSet {
UIView.animate(withDuration: 0.3) {
self.backgroundColor = self.isSelected ? .systemGray4 : .systemGray6
}
}
}

iOS8 Swift - Table View - Controller does not conform to protocol UITableViewDataSource

I added another model to my database, and I am essentially re-creating the same methods for this model as the previous one, however this time around I am getting the following error:
ContactsDetailViewController.swift:11:1: Type 'ContactsDetailViewController' does not conform to protocol 'UITableViewDataSource'
EDIT: I'm also getting these errors, but again, I don't see where the issue is:
/Volumes/BigMan/Code/Swift/BackpackerSpots/UIKit.UITableViewDataSource:3:48: Protocol requires function 'tableView(_:cellForRowAtIndexPath:)' with type '(UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell'
/Volumes/BigMan/Code/Swift/BackpackerSpots/BackpackerSpots/ContactsDetailViewController.swift:14:19: Candidate is not a function
Here is my ContactsDetailViewController:
import UIKit
class ContactsDetailViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate {
#IBOutlet var tableView:UITableView!
var contact:Contact?
override func viewDidLoad() {
super.viewDidLoad()
// customizing background of tableview
self.tableView.backgroundColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.2)
// remove extra separators
self.tableView.tableFooterView = UIView(frame: CGRectZero)
// change the color of the separator
self.tableView.separatorColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.8)
// self-sizing cells
tableView.estimatedRowHeight = 36.0
tableView.rowHeight = UITableViewAutomaticDimension
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int)
-> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:
NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as ContactsDetailTableViewCell
// make cell transparent so background color can be seen
cell.backgroundColor = UIColor.clearColor()
switch indexPath.row {
case 0:
cell.fieldLabel.text = "Name"
cell.valueLabel.text = contact?.contactName
case 1:
cell.fieldLabel.text = "Email"
cell.valueLabel.text = contact?.contactEmail
cell.mapButton.hidden = false
default:
cell.fieldLabel.text = ""
cell.valueLabel.text = ""
}
return cell
}
}
Here is the ContactsDetailTableViewCell:
import UIKit
class ContactsDetailTableViewCell: UITableViewCell {
#IBOutlet weak var fieldLabel:UILabel!
#IBOutlet weak var valueLabel:UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I must be overlooking something relatively simple, but I just don't see it and I've been struggling with this for hours. Any suggestions would be much appreciated. Thank you.
The are few things in the above code I would like to point out.
In cellForRowAtIndexPath method, you are using the cell identifier as "Cell", make sure you are using the same name in Storyboard and its unique across the project.
In cellForRowAtIndexPath method, brackets are not rightly marked
Try writing the following line for your cell:
let cell : ContactsDetailTableViewCell = tableView.dequeueReusableCellWithIdentifier("ContactsDetailTableViewCell", forIndexPath: indexPath) as ContactsDetailTableViewCell
Note: Make sure you rename identifier in the storyboard too.
I think you should add override:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}

Resources