How to navigate from UITableViewCell to UICollectionViewController programmatically in Swift 4 - ios

Here is what I want to achieve: When I tap a UITabViewCell (Picture 1), it will navigate to an UICollectionViewController (Picture 2). Just like the following two pictures showing:
I could navigate to an UIViewController when tap a UITableViewCell, but it doesn't work when I try to navigate to UICollectionView. Here are my codes:
import UIKit
class RealUserProfileController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let me = ["Vincent-St"]
private let followers = ["58 Followers"]
private let myPhotos = ["New Followers"]
private let others = ["My likes", "Notifications", "Clear Caches", "Drafts"]
private let settings = ["Settings"]
private let share = ["Share with friends"]
private let images = ["Hearts.png", "Footprint.png", "Notifications.png", "Trash-Empty.png"]
private let sections = ["me", "myPhotos", "others", "settings", "share"]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: UIFont(name: "Arial", size: 18)!, NSAttributedStringKey.foregroundColor: UIColor.black]
navigationItem.title = "Profile"
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
let myTableView: UITableView = UITableView(frame: CGRect(x: 0, y: 0, width: displayWidth, height: displayHeight), style: .grouped)
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
view.backgroundColor = UIColor(white: 1, alpha: 0.95)
myTableView.translatesAutoresizingMaskIntoConstraints = false
myTableView.topAnchor.constraint(equalTo: view.topAnchor, constant: -20).isActive = true
myTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
myTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 72
}
return tableView.rowHeight
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
let controller = UserProfileController()
self.navigationController?.pushViewController(controller, animated: true)
print("Value: \(me[indexPath.row])")
} else if indexPath.section == 1 {
print("Value: \(myPhotos[indexPath.row])")
} else if indexPath.section == 2 {
print("Value: \(others[indexPath.row])")
} else if indexPath.section == 3 {
let controller = ResultController()
navigationController?.pushViewController(controller, animated: true)
print("Value: \(settings[indexPath.row])")
} else if indexPath.section == 4 {
print("Value: \(share[indexPath.row])")
let shareText = "Share our app"
let shareImage = UIImage(named: "bell_unselected.png")
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [shareText, shareImage as Any], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
// return the number of cells each section.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return me.count
} else if section == 1 {
return myPhotos.count
} else if section == 2 {
return others.count
} else if section == 3 {
return settings.count
} else if section == 4 {
return share.count
} else {
return 0
}
}
// return cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "MyCell")
if indexPath.section == 0 {
cell.textLabel?.text = "\(me[indexPath.row])"
cell.detailTextLabel?.text = "\(followers[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "profile_image")
} else if indexPath.section == 1 {
cell.textLabel?.text = "\(myPhotos[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "icon_friends")
} else if indexPath.section == 2 {
cell.textLabel?.text = "\(others[indexPath.row])"
cell.imageView?.image = UIImage.init(named: images[indexPath.row])
} else if indexPath.section == 3 {
cell.textLabel?.text = "\(settings[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "Icon_Settings")
} else if indexPath.section == 4 {
cell.textLabel?.text = "\(share[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "Share")
}
cell.accessoryType = .disclosureIndicator
return cell
}
}
The two lines of codes will crash the app:
let controller = UserProfileController()
self.navigationController?.pushViewController(controller, animated: true)
Here is the error message:
Cannot convert value of type 'UserProfileController.Type' to expected argument type 'UIViewController'
Here are the codes of the destination UICollectionViewController:
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
}
After some research, I didn't find any useful information. Is there any way to make it happen? Or is there any way to implement the Picture 2 in another way, maybe: UIViewController, instead of UICollectionView?
Any suggestion would be appreciated.

I don't think the problem is with navigation, any subclass of UIViewController should be navigable.
I believe you need to initialize UICollectionViewController using init(collectionViewLayout: UICollectionViewLayout), collectionView has to have the layout set, otherwise the collectionView will crash if it gets presented.
Therefore instead of:
let controller = UserProfileController()
try using:
let controller = UserProfileController(collectionViewLayout: UICollectionViewFlowLayout())
Here I am assuming that the following code is your full implementation of UserProfileController, thus you directly inherit initializers from UICollectionViewController:
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
}

Related

UITableView not updating data

I have trying for sometime to make the "reloadData()" work but it is not working. I've even tried "DispatchQueue.main.async{self.tableView.reloadData()}"
Below is the code
import UIKit
import QuartzCore
import GameKit
enum Tags: Int {
case levelLabel = 100
case background = 101
case highScoreLabel = 102
case star1 = 201
case star2 = 202
case star3 = 203
}
let LightBlue = UIColor.systemBlue
let Green = UIColor.systemRed
let Purple = UIColor.systemPurple
let Pink = UIColor.systemPink
let Orange = UIColor.systemOrange
let OneStarScore = 5
let TwoStarScore = 10
let ThreeStarScore = 15
let NumLevels = 11
class HomeViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate, GKGameCenterControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var highScores: [Int] = [Int]()
var unlockedLevels = 0
let colors = [LightBlue, Green, Purple, Pink, Orange]
var tutorialPageVC: PageDataSourceViewController?
var gameVC: GameViewController?
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 13.0, *) {
tableView.separatorColor = UIColor.systemBackground
} else {
// Fallback on earlier versions
}
let storyboard = self.storyboard
tutorialPageVC = storyboard?.instantiateViewController(withIdentifier: "PageDataSourceVC")
as? PageDataSourceViewController
gameVC = storyboard?.instantiateViewController(withIdentifier: "GameViewController")
as? GameViewController
// Trying to fix UITableView
/// Put code here
self.tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(false)
let defaults = UserDefaults.standard
if let scoresArray = defaults.array(forKey: "highScores") {
highScores = scoresArray as! [Int]
unlockedLevels = highScores.count - 1 // Subtract one for tutorial
if highScores[unlockedLevels] >= 5 {
unlockedLevels += 1 // Unlock additional level if last high score is greater than 5
}
}
}
// MARK: - TableView data source methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return NumLevels
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelCell", for: indexPath)
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let view = cell.viewWithTag(Tags.background.rawValue) as UIView?
let levelLabel = cell.viewWithTag(Tags.levelLabel.rawValue) as! UILabel
let scoreLabel = cell.viewWithTag(Tags.highScoreLabel.rawValue) as! UILabel
let star1 = cell.viewWithTag(Tags.star1.rawValue) as! UIImageView
let star2 = cell.viewWithTag(Tags.star2.rawValue) as! UIImageView
let star3 = cell.viewWithTag(Tags.star3.rawValue) as! UIImageView
view?.layer.borderColor = colors[indexPath.row % 5].cgColor
view?.layer.borderWidth = 6.0
if indexPath.row == 0 {
// Tutorial Level
view?.alpha = 1.0
levelLabel.text = "Tutorial"
star1.isHidden = true
star2.isHidden = true
star3.isHidden = true
scoreLabel.text = ""
} else if indexPath.row + 1 <= highScores.count {
// Levels that have been played
view?.alpha = 1.0
levelLabel.text = "Level \(indexPath.row)"
star1.isHidden = false
star2.isHidden = false
star3.isHidden = false
formatStars(highScores[indexPath.row], stars: [star1, star2, star3])
scoreLabel.text = "High Score: \(highScores[indexPath.row])"
} else {
// Levels that have NOT been played
view?.alpha = indexPath.row > unlockedLevels ? 0.5 : 1.0
levelLabel.text = "Level \(indexPath.row)"
star1.isHidden = true
star2.isHidden = true
star3.isHidden = true
scoreLabel.text = ""
}
return cell
}
/// Display stars for each level on homepage
func formatStars(_ score: Int, stars: [UIImageView]) {
switch score {
case 0..<OneStarScore:
stars[0].image = UIImage(named: "star-empty")
stars[1].image = UIImage(named: "star-empty")
stars[2].image = UIImage(named: "star-empty")
case OneStarScore..<TwoStarScore:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star-empty")
stars[2].image = UIImage(named: "star-empty")
case TwoStarScore..<ThreeStarScore:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star")
stars[2].image = UIImage(named: "star-empty")
default:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star")
stars[2].image = UIImage(named: "star")
}
}
// MARK: - TableView delegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
self.present(tutorialPageVC!, animated: true, completion: nil)
} else if indexPath.row <= unlockedLevels {
gameVC!.level = indexPath.row
self.present(gameVC!, animated: true, completion: nil)
// performSegueWithIdentifier("LevelSegue", sender: self)
}
}
Maybe the connection between the UITableView and the delegate and data source are not set.
You could do this in the storyboard by ctrl+dragging from the table view to the view controller and setting the delegate and data source outlets.
Or you can do so in code, e.g. in viewDidLoad:
self.tableView.dataSource = self
self.tableView.delegate = self
You need to set dataSource and delegate to self
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.datasource = self
}
And Assign values directly like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelCell", for: indexPath) as? LevelCell
cell?.star1.isHidden = true
}

Retrieving textfield data from custom cell from another view outside of the table view

I have a view with an embedded UITableViewController that is filled with custom cells. When I hit the save button I would like the getProjectName() within the UITableViewController to return the projectNameTF data within in the custom cell. Currently when I try to get the cell within the getProjectName() it returns a nil cell.
Main View:
class NewProjectView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveBtn(_ sender: UIButton) {
print("Save button hit")
print(NewProjectTableViewController().getProjectName())
}
}
Embedded TableViewController
import UIKit
struct cellType{
var mainTitle = String()
var numOfChildCells = Int()
var opened = Bool()
}
class NewProjectTableViewController: UITableViewController {
var tableViewData = [cellType]()
var customCellData = [UITableViewCell]()
var projectNameTFR = UITextField()
// Counts the number of cells and displays them
override func numberOfSections(in tableView: UITableView) -> Int {
return tableViewData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If the parent cell is opened display the number of cells inside it
if tableViewData[section].opened == true {
return tableViewData[section].numOfChildCells + 1
}
else {
return 1
}
}
//
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.tableView.separatorStyle = .none
// Do this for the header cell
if indexPath.row == 0{
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as? HeaderCell else {return UITableViewCell()}
cell.backgroundColor = .clear
cell.setUpCell(title: tableViewData[indexPath.section].mainTitle)
// If cell should be opened, display correct open image
if tableViewData[indexPath.section].opened{
cell.openCell()
}
// else display closed image
else{
cell.closeCell()
}
return cell
// else it is a child cell
}else {
switch tableViewData[indexPath.section].mainTitle{
// Load Project info cell
case "Project Information":
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProjectNameCell") as? ProjectNameCell else {return UITableViewCell()}
projectNameTFR = cell.projectNameTF
return cell
case "Client Information":
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ClientInfoCell") as? ClientInfoCell else {return UITableViewCell()}
return cell
default:
print("defaulted cell")
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
return cell
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true{
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)
let headerCell = tableView.cellForRow(at: indexPath) as! HeaderCell
}
else{
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)
let headerCell = tableView.cellForRow(at: indexPath) as! HeaderCell
}
}
override func viewDidLoad() {
super.viewDidLoad()
//self.definesPresentationContext = true
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
// Do any additional setup after loading the view.
print("add new client screen loaded")
registerTableViewCells()
// Create the cells
tableViewData = [cellType(mainTitle: "Project Information", numOfChildCells: 1, opened: true ),
cellType(mainTitle: "Client Information", numOfChildCells: 1, opened: false )]
}
override func viewWillAppear(_ animated: Bool) {
// Add a background view to the table view
let backgroundImage = UIImage(named: "App Background.png")
let imageView = UIImageView(image: backgroundImage)
self.tableView.backgroundView = imageView
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
private func registerTableViewCells(){
let ClientInfoCell = UINib(nibName: "ClientInfoCell", bundle: nil)
self.tableView.register(ClientInfoCell, forCellReuseIdentifier: "ClientInfoCell")
let ProjectNameCell = UINib(nibName: "ProjectNameCell", bundle: nil)
self.tableView.register(ProjectNameCell, forCellReuseIdentifier: "ProjectNameCell")
let HeaderCell = UINib(nibName: "HeaderCell", bundle: nil)
self.tableView.register(HeaderCell, forCellReuseIdentifier: "HeaderCell")
let SaveCell = UINib(nibName: "SaveCell", bundle: nil)
self.tableView.register(SaveCell, forCellReuseIdentifier: "SaveCell")
}
func getProjectName() -> String{
let indexPath = NSIndexPath(row: 0, section: 0)
let cell = tableView?.cellForRow(at: indexPath as IndexPath) as? ProjectNameCell
print(type(of: cell))
if(cell==nil){
print("cell is nil")
}
return "I returned this test string"
}
}
Custom Cell I am trying to reach
import UIKit
class ProjectNameCell: UITableViewCell {
#IBOutlet weak var projectNameTF: UITextField!
var projectName = String()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
backgroundColor = .clear
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Well sir you are getting cell on index path 0 section number and 0th row
let indexPath = NSIndexPath(row: 0, section: 0)
on that index you have HeaderCell instead of ProjectNameCell thats why you are getting nil
this line can't cast your HeaderCell to ProjectNameCell
let cell = tableView?.cellForRow(at: indexPath as IndexPath) as? ProjectNameCell

Swift- Using Delegation to append data to TableView within CollectionView

I have a CollectionViewController named yearOnethat looks something like this:
When you click the add button for each quarter cell it leads you to SearchPage:
I used delegation to append the data clicked on SearchPage to yearOne
The delegation seems to work and data seems to append as I tried the print(vc.data) but it doesn't seem to show up on the tableView when backed into yearOne viewcontroller through navigationController?.popViewController(animated: true) Am I doing something wrong?
import UIKit
class yearOne: UICollectionViewController, coursesDelegate {
let customCellIdentifier = "cellID"
let customCellIdentifier2 = "cellID2"
let customCellIdentifier3 = "cellID3"
let customCellIdentifier4 = "cellID4"
let quarters = [
customLabel (title: "Fall Quarter"),
customLabel (title: "Winter Quarter"),
customLabel (title: "Spring Quarter"),
customLabel (title: "Summer Quarter")
]
let vc = fallQuarterCell()
let vc2 = winterQuarterCell()
let vc3 = springQuarterCell()
let vc4 = summerQuarterCell()
func sendDataBackFall(data: String) {
vc.data.append(data)
print(vc.data)
//UserDefaults.standard.set(vc.data, forKey: "SavedArray")
}
func sendDataBackWinter(data2: String) {
vc2.data.append(data2)
}
func sendDataBackSpring(data3: String) {
vc3.data.append(data3)
}
func sendDataBackSummer(data4: String) {
vc4.data.append(data4)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
vc.tableView.reloadData()
collectionView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
self.collectionView!.register(fallQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier)//
self.collectionView!.register(winterQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier2)
self.collectionView!.register(springQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier3)
self.collectionView!.register(summerQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier4)
navigationItem.title = "Year One"
navigationController?.navigationBar.prefersLargeTitles = true
collectionView?.backgroundColor = .lightGray
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return quarters.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (indexPath.row == 0){
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as! fallQuarterCell
cell1.layer.borderColor = UIColor.orange.cgColor
cell1.layer.borderWidth = 2
cell1.layer.cornerRadius = 5
cell1.quarters = self.quarters[0]
return cell1
}
else if (indexPath.row == 1){
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier2, for: indexPath) as! winterQuarterCell
cell2.layer.borderColor = UIColor.blue.cgColor
cell2.layer.borderWidth = 2
cell2.layer.cornerRadius = 5
cell2.quarters = self.quarters[1]
return cell2
}
else if (indexPath.row == 2){
let cell3 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier3, for: indexPath) as! springQuarterCell
cell3.layer.borderColor = UIColor.green.cgColor
cell3.layer.borderWidth = 2
cell3.layer.cornerRadius = 5
cell3.quarters = self.quarters[2]
return cell3
}
else if (indexPath.row == 3){
let cell4 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier4, for: indexPath) as! summerQuarterCell
cell4.layer.borderColor = UIColor.red.cgColor
cell4.layer.cornerRadius = 5
cell4.quarters = self.quarters[3]
return cell4
}
else{
return UICollectionViewCell()
}
}
#objc func buttonAction(sender: UIButton!) {
switch sender.tag {
case 0:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 1:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 2:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 3:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
default:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
}
}
}
extension yearOne : UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.frame.width - 30)
return CGSize(width: width, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 8
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
UIEdgeInsets(top: 30, left: 10, bottom: 30, right: 10)
}
}
class fallQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {
//var data = UserDefaults.standard.object(forKey: "SavedArray") as? [String] ?? [String]()
var data : [String] = []
// func load(){
// if let loadeddata: [String] = UserDefaults.standard.object(forKey: "SavedArray") as? [String] {
// data = loadeddata
// tableView.reloadData()
// }
// }
let cellId = "coursesName"
let tableView:UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = UIColor.white
return tableView
}()
override init(frame: CGRect){
super.init(frame: frame)
addSubview(tableView)
setupView()
}
func setupView(){
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.delegate = self
tableView.dataSource = self
self.backgroundColor = UIColor.white
contentView.addSubview(quarterLabel)
contentView.addSubview(addButton)
quarterLabel.translatesAutoresizingMaskIntoConstraints = false
quarterLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
quarterLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.topAnchor.constraint(equalTo: quarterLabel.topAnchor, constant: -5).isActive = true
addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
addButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
addButton.widthAnchor.constraint(equalToConstant: 25).isActive = true
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 35).isActive = true
tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = data[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
if (indexPath.row == 0){
data.remove(at: 0)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 1){
data.remove(at: 1)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 2){
data.remove(at: 2)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 3){
data.remove(at: 3)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 4){
data.remove(at: 4)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//add class information???
}
var quarters: customLabel? {
didSet {
guard let quarters = quarters else {return}
quarterLabel.text = quarters.title
}
}
let quarterLabel : UILabel = {
let label = UILabel()//frame: CGRect(x: 15, y: -75, width: 300, height: 50))
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
label.font = UIFont.boldSystemFont(ofSize: 16)
//label.textAlignment = .center
return label
}()
let addButton : UIButton = {
let button = UIButton()//frame: CGRect(x: 345, y: 10, width: 30, height: 30))
button.setImage(UIImage(named: "addicon"), for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.tag = 0
button.addTarget(self, action: #selector(yearOne.buttonAction), for: .touchUpInside)
return button
}()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also have the
class winterQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
class springQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
class summerQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
similar to fallQuarterCell but excluded it.
import UIKit
protocol coursesDelegate {
func sendDataBackFall(data: String)
func sendDataBackWinter(data2: String)
func sendDataBackSpring(data3: String)
func sendDataBackSummer(data4: String)
}
class SearchPage: UITableViewController {
var delegate: coursesDelegate?
let cellId = "course"
var allCourses : NSArray = NSArray()
var filteredCourses = [String]()
var resultSearchController = UISearchController()
var tag: Int?
let vc = fallQuarterCell()
let vc2 = winterQuarterCell()
let vc3 = springQuarterCell()
let vc4 = summerQuarterCell()
override func viewDidLoad() {
super.viewDidLoad()
let courses : CourseList = CourseList()
allCourses = courses.coursesList
navigationItem.title = "Select Courses"
navigationController?.navigationBar.prefersLargeTitles = true
//let button1 = UIBarButtonItem(title: "Add Course", style: .plain, target: self, action: #selector(addTapped))
//self.navigationItem.rightBarButtonItem = button1
self.view.backgroundColor = .systemBackground
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.delegate = self
tableView.dataSource = self
self.tableView.tableFooterView = UIView()
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.obscuresBackgroundDuringPresentation = false
controller.searchBar.placeholder = "Search Courses"
controller.searchBar.sizeToFit()
tableView.tableHeaderView = controller.searchBar
return controller
})()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (resultSearchController.isActive) {
return filteredCourses.count
}
else{
return allCourses.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
if (resultSearchController.isActive) {
cell.textLabel?.text = filteredCourses[indexPath.row]
return cell
}
else {
let courses = self.allCourses[indexPath.row]
cell.textLabel?.text = courses as? String
return cell
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
tableView.deselectRow(at: indexPath, animated: true)
if (tag == 0){
if (resultSearchController.isActive){
// vc.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc.data, forKey: "SavedArray")
// self.navigationController?.popViewController(animated: true)
let data = filteredCourses[indexPath.row]
delegate?.sendDataBackFall(data: data)
self.navigationController?.popViewController(animated: true)
}
else{
// vc.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc.data, forKey: "SavedArray")
// self.navigationController?.popViewController(animated: true)
let data = allCourses[indexPath.row] as! String
delegate?.sendDataBackFall(data: data)
self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 1){
if (resultSearchController.isActive){
// vc2.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc2.data, forKey: "SavedArray2")
// self.navigationController?.popViewController(animated: true)
let data = filteredCourses[indexPath.row]
delegate?.sendDataBackWinter(data2: data)
self.navigationController?.popViewController(animated: true)
}
else{
// vc2.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc2.data, forKey: "SavedArray2")
// self.navigationController?.popViewController(animated: true)
let data = allCourses[indexPath.row] as! String
delegate?.sendDataBackWinter(data2: data)
self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 2){
if (resultSearchController.isActive){
// vc3.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc3.data, forKey: "SavedArray3")
// self.navigationController?.popViewController(animated: true)
}
else{
// vc3.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc3.data, forKey: "SavedArray3")
// self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 3){
if (resultSearchController.isActive){
// vc4.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc4.data, forKey: "SavedArray4")
// self.navigationController?.popViewController(animated: true)
}
else{
// vc4.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc4.data, forKey: "SavedArray4")
// self.navigationController?.popViewController(animated: true)
}
}
}
var isSearchBarEmpty: Bool {
return resultSearchController.searchBar.text?.isEmpty ?? true
}
}
extension SearchPage: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filteredCourses.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (allCourses as NSArray).filtered(using: searchPredicate)
filteredCourses = array as! [String]
self.tableView.reloadData()
}
}
I commented out the UsersDefault.standard.set.... because the data itself doesn't seem to refresh and display in the tableView atm
Check at your cellForRow method. I don't believe you set the text anywhere.

How to navigate to viewController from tableview cell, which is placed in other tableview Cell?

I have placed a tableview2 in tableview1 Cell, Now when I click on the tableview2 cell I need to navigate to a new viewController. Please help me... I was struggling with one whole day :(
here is the code, the second table view is placed in SegmentedCell...
when i am trying to push, its unable to go next controller..
import UIKit
import XMSegmentedControl
import Alamofire
import SwiftyJSON
class segmentedCell: UITableViewCell, XMSegmentedControlDelegate, UITableViewDelegate, UITableViewDataSource{
let byndrColor : UIColor = UIColor( red: 224/255, green: 0/255, blue: 115/255, alpha: 1.0 )
let fontStyle = UIFont(name: "Lato-bold", size: 12)
#IBOutlet weak var segmentedControl: XMSegmentedControl!
#IBOutlet weak var feedTableView: UITableView!
var getApi = UIApplication.shared.delegate as! AppDelegate
var course_id = String()
var materialListObjects = [MaterialsInSingleCourseGetSet]()
var assignmentExamAndQuizListObjects = [AssignmentAndExamsQuizGetSet]()
override func awakeFromNib() {
super.awakeFromNib()
feedTableView.delegate = self
feedTableView.dataSource = self
segmentedControl.delegate = self
segmentedControl.segmentTitle = ["LATEST", "MATERIALS", "COURSEWORK", "PROGRESS"]
segmentedControl.font = fontStyle!
segmentedControl.selectedItemHighlightStyle = XMSelectedItemHighlightStyle.BottomEdge
segmentedControl.backgroundColor = UIColor.white
segmentedControl.tint = UIColor.black
segmentedControl.highlightTint = byndrColor
segmentedControl.highlightColor = byndrColor
segmentedControl.edgeHighlightHeight = 2
segmentedControl.selectedSegment = 0
let share = UIApplication.shared.delegate as! AppDelegate
materialListObjects = share.materialListInSingleCourse as! [MaterialsInSingleCourseGetSet]
assignmentExamAndQuizListObjects = share.assignmentsExamsAndQuizListInSingleCourse as! [AssignmentAndExamsQuizGetSet]
// Initialization code
}
func xmSegmentedControl(xmSegmentedControl: XMSegmentedControl, selectedSegment: Int) {
if xmSegmentedControl == segmentedControl {
print("SegmentedControl1 Selected Segment: \(selectedSegment)")
switch segmentedControl.selectedSegment
{
case 0:
feedTableView.reloadData()
case 1:
feedTableView.reloadData()
case 2:
feedTableView.reloadData()
case 3:
feedTableView.reloadData()
default :
break
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentedControl.selectedSegment == 0
{
return 0
}
else
if segmentedControl.selectedSegment == 1
{
return materialListObjects.count
}
else
if segmentedControl.selectedSegment == 2
{
return assignmentExamAndQuizListObjects.count
}
else
{
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if segmentedControl.selectedSegment == 0
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
if segmentedControl.selectedSegment == 1
{
if materialListObjects[indexPath.row].type == "file"
{
let cell = Bundle.main.loadNibNamed("materialCellOne", owner: self, options: nil)?.first as! materialCellOne
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
let image = materialListObjects[indexPath.row].title
cell.contentImage.image = image.documentType(givenType: image)
return cell
}else
{
let cell = Bundle.main.loadNibNamed("materialCellTwo", owner: self, options: nil)?.first as! materialCellTwo
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
cell.contentImage.image = #imageLiteral(resourceName: "material_hyperlink")
return cell
}
}
else
if segmentedControl.selectedSegment == 2
{
let cell = Bundle.main.loadNibNamed("CourseWorkCell", owner: self, options: nil)?.first as! CourseWorkCell
print("assignment title : \(assignmentExamAndQuizListObjects[indexPath.row].title)")
cell.titleLabel.text = assignmentExamAndQuizListObjects[indexPath.row].title
if assignmentExamAndQuizListObjects[indexPath.row].type == ""
{
cell.contentImage.image = #imageLiteral(resourceName: "assignment_large")
}else
{
cell.contentImage.image = #imageLiteral(resourceName: "exam_inline")
}
var time = assignmentExamAndQuizListObjects[indexPath.row].start
time = time.dateRange(dateString: time)
time = time.days(givenDate: time)
cell.timeLabel.text = time
return cell
}
else
if segmentedControl.selectedSegment == 3
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if segmentedControl.selectedSegment == 2
{
return 70
}
else
{
return 100
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect.zero)
let label = UILabel(frame: CGRect(x: 8, y: 8, width: 150, height: 20))
view.addSubview(label)
label.font = UIFont(name: "Lato-Heavy", size: 17)
if segmentedControl.selectedSegment == 1
{
switch section {
case 0:
label.text = "All Materials"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
if segmentedControl.selectedSegment == 2
{
switch section {
case 0:
label.text = "All CourseWork"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
{
}
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
//How to perform from here
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if segmentedControl.selectedSegment == 1
{
let storyboard = UIStoryboard(name: "Main", bundle : nil)
let nextViewController = storyboard.instantiateViewController(withIdentifier: "QuickLook") as! QuickLook
if materialListObjects[indexPath.row].type == "url"
{
nextViewController.id = materialListObjects[indexPath.row].body
nextViewController.type = "url"
}
else
{
nextViewController.id = materialListObjects[indexPath.row].id
}
nextViewController.course_id = String(describing: materialListObjects[indexPath.row].course_id)
let naviControl = UINavigationController(rootViewController: nextViewController)
naviControl.pushViewController(nextViewController, animated: true)
}
}
}
I have created a similar scenario as yours and this is how you can get it working.
1. View Hierarchy
I have used tag property to uniquely identify both UITableViews, i.e.
Outer tableView tag = 0
Inner tableView tag = 1
2. Now implement UITableViewDataSource, UITableViewDelegate methods for both the tableViews. Set the dataSource and delegate of both the tableViews as the ViewController.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView.tag == 0
{
return 1
}
else if tableView.tag == 1
{
return 5
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView.tag == 0
{
return tableView.dequeueReusableCell(withIdentifier: "outercell", for: indexPath)
}
else if tableView.tag == 1
{
return tableView.dequeueReusableCell(withIdentifier: "innercell", for: indexPath)
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableView.tag == 1
{
//TODO: Write your code for navigating to another ViewController here
print("Inner cell tapped")
}
}
Edit:
In the Interface Builder, you can find a tag attribute corresponding to each element in the attributes inspector, i.e.
For outer tableView set it to 0 and for inner tableView set it to 1.
Let me know if you still face any issues. Happy Coding..🙂
Use performSegue(withIdentifier: "ViewController", sender: self); in didSelectrow method of you tableView2.

Only Single cell expands at a time in UITableViewCell in swift3

I am implementing an expandable and collapsable table cell on the click of a button in the custom table cell. I have tried the following code but it expands only single cell at a time. That is if I click a cell it expands but if I click on another cell it expands and the already expanded cell collapses.
var selectedIndexPath: IndexPath?
func configure(cell: MyProposalCustomCell, forRowAtIndexPath indexPath: IndexPath) {
let pool = myProposalsDetails[indexPath.row]
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsets.zero
cell.layoutMargins = UIEdgeInsets.zero
cell.proposalNumber.text = pool.proposalNumber
cell.pickUpLocation.text = pool.pickupLocation
cell.dropLocation.text = pool.dropLocation
cell.journeyType.text = pool.journeyType
cell.firstShiftOnwardTime.text = pool.firstPickupTime
cell.firstShiftReturnTime.text = pool.firstEndTime
if let numberOfInterests = pool.noOfInterest {
cell.numberOfInterest.text = String(numberOfInterests)
}
if let operatingDays = pool.operatingDays {
cell.daysOfOperation.attributedText = Utility.getProposedOperatingDays(operatingDays: operatingDays)
}
cell.expandCollapse.tag = indexPath.row
cell.expandCollapse.addTarget(self, action: #selector(expandTableCell(sender:)), for: .touchUpInside)
if selectedIndexPath == indexPath {
UIView.animate(withDuration: 0.3, animations: {
cell.backgroundColor = CustomColor.selectedBackground.color
cell.bottomView.backgroundColor = CustomColor.selectedBackground.color
cell.expandCollapse.setImage(UIImage(named: "collapse_arrow.png"), for: .normal)
if let proposedStartDate = pool.startDate {
let propStartDate = Date(timeIntervalSince1970: proposedStartDate)
cell.proposedStartDate.text = Utility.getFormattedDate(date: propStartDate)
cell.proposedStartDateTxt.text = NSLocalizedString("Proposed start date", comment: "")
}
cell.returnTime.alpha = 0.0
})
} else {
UIView.animate(withDuration: 0.3, animations: {
cell.backgroundColor = UIColor.white
cell.expandCollapse.setImage(UIImage(named: "down_arrow.png"), for: .normal)
cell.proposedStartDateTxt.text = NSLocalizedString("Journey type", comment: "")
cell.bottomView.backgroundColor = UIColor.white
cell.proposedStartDate.text = pool.journeyType
cell.returnTime.isHidden = false
})
}
}
This is the expandable button action:
func expandTableCell(sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
if selectedIndexPath == indexPath {
selectedIndexPath = nil
} else {
let previousSelIndex = selectedIndexPath
selectedIndexPath = indexPath
if let previousSelectedIndexPath = previousSelIndex {
if tripStatus.tripType != .splitShift {
if let previousSelectedCell = myProposals.cellForRow(at: previousSelectedIndexPath) as? MyProposalCustomCell {
configure(cell: previousSelectedCell, forRowAtIndexPath: previousSelectedIndexPath)
}
} else {
if let previousSelectedCell = myProposals.cellForRow(at: previousSelectedIndexPath) as? MyProposalSplitShiftCell {
configureSplitShift(cell: previousSelectedCell, forRowAtIndexPath: previousSelectedIndexPath)
}
}
}
}
updateSelectedCell(indexPath: indexPath)
myProposals.beginUpdates()
myProposals.endUpdates()
}
func updateSelectedCell(indexPath: IndexPath) {
if tripStatus.tripType != .splitShift {
if let selectedCell = myProposals.cellForRow(at: indexPath) as? MyProposalCustomCell {
configure(cell: selectedCell, forRowAtIndexPath: indexPath)
}
} else {
if let selectedCell = myProposals.cellForRow(at: indexPath) as? MyProposalSplitShiftCell {
configureSplitShift(cell: selectedCell, forRowAtIndexPath: indexPath)
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let headingHeight: CGFloat = 128
let detailsHeight: CGFloat = 80
let splitShiftDetailsHeight: CGFloat = 215
switch tripStatus.tripType {
case .oneWayTrip, .roundTrip:
if selectedIndexPath != nil && indexPath.compare(selectedIndexPath! as IndexPath) == ComparisonResult.orderedSame {
return headingHeight + detailsHeight
}
return headingHeight
case .splitShift:
if selectedIndexPath != nil && indexPath.compare(selectedIndexPath! as IndexPath) == ComparisonResult.orderedSame {
return headingHeight + splitShiftDetailsHeight
}
return headingHeight
}
}
I want to get multiple cells to be expanded. How to achieve this?
This is very straight forward, instead of taking selectedIndexPath try to keep one variable in your custom UITableViewCell class something like,
class ExpandableCell: UITableViewCell {
var isExpanded: Bool = false
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
}
#IBAction func moreButtonTapped(_ sender: Any) {
}
}
And then try to toggle the isExpanded boolean flag, and try to perform your func updateSelectedCell(indexPath: IndexPath) {} action.

Resources