I have a storyboard with two buttons, each one with target to another storyboard with a TableView but different segue identifier.
Right now I populate my data source through a two dimension array
var dataArray = [["a", "b", "c"], ["menu 1", "menu 2", "menu 3"]]
What I would like to do is based on the button selection hide a TableView section.
Example:
Selected button 1 hide section 2 on my TableView.
&
Selected button 2 hide section 1 on my TableView.
My data source extension looks like this
extension ViewControllerTwo: UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0{
return "Section 1"
}
return "Section 2"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
if section == 0{
return "Number of items in section 1 is \(dataArray[0].count)"
}
return "Number of items in section 2 is \(dataArray[1].count)"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "singleCell")
if cell == nil{
cell = UITableViewCell(style: .default, reuseIdentifier: "singleCell")
cell?.accessoryType = .detailButton
}
cell!.textLabel?.text = dataArray[indexPath.section][indexPath.row]
return cell!
}
}
The two viewControllers are named as follow
"ViewController"
"ViewControllerTwo"
Think of the segues identifiers named as follow
"segueItemsOption" for button 1
"segueMenuOption" for button 2
Edit
View Controller Code.
Haven't really modified anything as segue automatically displays the next VC.
import UIKit
class ViewController: UIViewController {
// MARK: Outlets
#IBOutlet weak var itemsButton: UIButton!
#IBOutlet weak var menuButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Buttons Rounded Corner
itemsButton.round()
itemsButton.round()
}
// MARK: Button Actions
#IBAction func itesmButtonAction(_ sender: Any) {
createVolumes.shine()
}
#IBAction func menuButtonAction(_ sender: Any) {
listVolumes.shine()
}
To achieve what you want you can try following:
Create an enum:
enum ShowCase: Int{
case items, menu
var headerTitle: String{
switch self {
case .items:
return "Section 1"
case .menu:
return "Section 2"
}
}
static func createFromSegueIdentifier(_ str: String) -> ShowCase{
str == "segueItemsOption" ? .items : .menu
}
}
In you ViewController add:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let vc = segue.destination as? ViewControllerTwo, let identifier = segue.identifier else{
return
}
vc.showCase = ShowCase.createFromSegueIdentifier(identifier)
}
in ViewcontrollerTwo add an implicitly unwrapped var:
var showCase: ShowCase!
and the extension should look like:
extension ViewControllerTwo: UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
// return only one section
1
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
showCase.headerTitle
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
"Number of items in \(showCase.headerTitle.lowercased()) is \(dataArray[showCase.rawValue].count)"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray[showCase.rawValue].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "singleCell")
if cell == nil{
cell = UITableViewCell(style: .default, reuseIdentifier: "singleCell")
cell?.accessoryType = .detailButton
}
cell!.textLabel?.text = dataArray[showCase.rawValue][indexPath.row]
return cell!
}
}
Ugh, try creating an if condition for each segue and .isHidden property.
That works 100% fam
Related
I am trying to create UITableview with cells, identical to the iPhone settings screenshot.
It is part of my homework so i have to do it all in UITableview.
this is what I did with my code, but everything is red and full with errors. I tried to do it following the samples from lessons but it kinda looks all wrong.
Please, help me understand how this thing works and what is wrong.
import UIKit
struct Lines{
var image: [UIImage] = []
var title: [String] = []
}
class Titles {
static func titles() -> [Lines]{
return [
Lines(image: UIImage[ systemName: "airplane" ,"wifi.square.fill", "bitcoinsign.circle.fill", "iphone.homebutton.radiowaves.left.and.right", "personalhotpot" ], title: ["Авиарежим" , "Wi-fi", "Bluetooth", "Сотовая связь", "Режим модема"]),
Lines(image: UIImage[ systemName: "bell.badge.fill" ,"speaker.wave.3.fill", "moon.fill", "iphone.homebutton.radiowaves.left.and.right", "clock.fill" ], title: ["Уведомления", "Звуки,тактильные сигналы", "Не беспокоить", "Экранное время"]),
Lines(image: UIImage[ systemName: "gear" ,"switch.2", "display" ] , title: ["Общие", " Control Centre", "Экран и яркость"])
]
}
}
class SecondTableViewController: UITableViewController {
var lines = Titles.titles()
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension SecondTableViewController: UITableViewDataSource, UITableViewDelegate{
func numberOfSections(in tableView: UITableView) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles[section].title.count
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "SectionCell") as! TableViewCell
let title = titles[section]
cell.image = Lines.image
cell.titleLabel.text = Lines.title
return cell
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SecondTableViewCell") as! TableViewCell
let name = titles[indexPath.section].title[indexPath.row]
cell.image = Lines.image
cell.titleLabel.text = Lines.title
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
Thank you!
You use the wrong property name :
func numberOfSections(in tableView: UITableView) -> Int {
// titles is not defined
return titles.count
}
You must use :
func numberOfSections(in tableView: UITableView) -> Int {
return lines.count
}
Same for the other methods. In cellForRow :
let name = titles[indexPath.section].title[indexPath.row]
Let image = titles[indexPath.section].image[indexPath.row]
Should be replaced by :
let name = lines[indexPath.section].title[indexPath.row]
For viewFirHeader you use a cell which is usually not what is done. You have no title for each lines array. You may have to think again what you want to use. The way you organise your data may also be rethink as you have 2 different array for images and titles.
Right now I am trying to move information from my goal cell into a new table view cell, and am having difficulty getting the cell to display.
Here is the code for my goal cell.
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var Goals: [String] = ["goal 1", "goal 2", "goal 3"]
let theEmptyModel: [String] = ["No data in this section."]
var valueToPass = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func showGoalSelected() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
let popUp = GoalSelectedPopUp()
self.view.addSubview(popUp)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "GoalConversationsCell_1") {
let viewController = segue.destination as! ActiveGoalsViewController
viewController.Goals.append([valueToPass])
}
}
}
extension GoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalCell_1", for: indexPath)
cell.textLabel?.text = Goals[indexPath.row]
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.textLabel?.numberOfLines = 3
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
valueToPass = Goals[indexPath.row]
performSegue(withIdentifier: "activeGoalsSegue", sender: self)
Goals.remove(at: indexPath.row)
if Goals.count != 0 {
showGoalSelected()
} else {
Goals.append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
Here is the goal cells storyboard with the push segue connecting it to the other table view.
That other table view is shown below.
Here is the code for this new tableview.
import UIKit
class ActiveGoalsViewController: UIViewController {
#IBOutlet weak var goalTableView: UITableView!
let sections: [String] = ["Mark as Complete:", "History:"]
var goals: [[String]] = [[], []]
let theEmptyModel: [String] = ["No data in this section."]
extension ActiveGoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TodayGoalViewCell_1", for: indexPath) as? GoalTableViewCell
cell?.goalLabel.text = Goals[indexPath.section][indexPath.row]
cell?.cellDelegate = self
cell?.index = indexPath
return cell!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if Goals[0] != theEmptyModel {
Goals[1].append(Goals[0][indexPath.row])
if Goals[1].first!.contains("No data in this section.") {
Goals[1].removeFirst()
}
Goals[0].remove(at: indexPath.row)
if Goals[0].count == 0 {
Goals[0].append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
}
Once the goal is selected, it sends me to the new storyboard, but this new view does not display the goal that was just added. Can someone help me figure out why this isn't working? Thanks.
I think in the second view controller you need to access the "goals" variable with a lower case g rather then the "Goals" variable with an upper case G.
I try to make a multi section UITableView. I get it work if I only add two strings into my "overview" array. But when I try to call my class "Player" and "Comepetitions" I don't make it work. I have checked and both classes have elements.
//My player and Comepetitions class
var comepetition = [Comepetitions]() //Tävlingar
var players = [Player]()//Spelare
let sections = ["Tävlingar","Spelare"]
//here I want to replace my strings to my classes (player and Comepetitions class)
var overview = [[Player](),[Comepetitions]()] as [Any]
override func viewDidLoad() {
super.viewDidLoad()
print(overview)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (overview[section] as AnyObject).count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ConconfirmCell", for: indexPath)
cell.textLabel?.text = overview[indexPath.section] as? String
cell.textLabel?.textColor = UIColor.white
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 20.0)
return cell
}
//All Information how wants to follow the Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//Segue for start a new game
if segue.identifier == "startNewGameSegue" {
let destVC=segue.destination as! GameViewController
destVC.competitions = comepetition as [Comepetitions]
destVC.players = players
}
}
}
This code works!
var comepetition = [Comepetitions]() //Tävlingar
var players = [Player]()//Spelare
let sections = ["Tävlingar","Spelare"]
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == 0) {
return comepetition.count
} else {
return players.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ConconfirmCell", for: indexPath)
if (indexPath.section == 0) {
cell.textLabel?.text = comepetition[indexPath.row].comepetitionsOption
}else{
cell.textLabel?.text = players[indexPath.row].name
}
cell.textLabel?.textColor = UIColor.white
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 20.0)
return cell
}
//All Information how wants to follow the Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//Segue for start a new game
if segue.identifier == "startNewGameSegue" {
let destVC=segue.destination as! GameViewController
destVC.competitions = comepetition as [Comepetitions]
destVC.players = players
}
}
}
I think it's because of this line, it's optional and you should unwrap it but in the code you post there is no optional checking.
var comepetition = [Comepetitions?]()
And could you add the code that has problem because with code you post here the is no way to know witch is going to be section and witch is the items for that section.
Hope this will helps.
I have an Expanded TableView, with HeaderView containing a checkBox, a label and a radioButton.
CheckBoxes can have multiple selection. But radioButton has only single selection. If another radio-button is selected, then previously selected button gets deselected in tableView
My problem is, whenever I select a radio-button at section 0, then radio-button at section 8 and 16 also get selected. Upon scrolling the radio-button changes it's state. Any radio-button for any section gets selected. I am aware it is due to the cell reuse property of the tableView, But I am not getting how to solve this. I have referred to numerous solutions here on SO, but none seemed to work. This issue is really troublesome for me, because of which I am not able to proceed further. Kindly guide me if wrong or if I am missing something. Help much appreciated. Thank you!
Here is my code for HeaderView of tableView:
import UIKit
import M13Checkbox
protocol HeaderViewDelegate {
func toggleHeader(header : HeaderView, section : Int)
}
protocol CustomHeaderDelegate: class {
func didTapButton(in section: Int, headerView : HeaderView)
}
class HeaderView: UITableViewHeaderFooterView {
#IBOutlet weak var stateCheckBox: M13Checkbox!
#IBOutlet weak var stateNameLabel: UILabel!
#IBOutlet weak var favouriteState: M13Checkbox!
var delegate : HeaderViewDelegate?
weak var delegateHeader: CustomHeaderDelegate?
var sectionNumber : Int!
var section : Int!
var radioButtonSelected : Bool = false {
didSet {
if radioButtonSelected {
favouriteState.checkState = .checked
}else{
favoriteState.checkState = .unchecked
}
}
}
override func awakeFromNib() {
stateCheckBox.boxType = .square
stateCheckBox = .bounce(.fill)
favouriteState.boxType = .circle
favouriteState.setMarkType(markType: .radio, animated: true)
favouriteState.stateChangeAnimation = .bounce(.stroke)
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
self.addGestureRecognizer(UITapGestureRecognizer(target : self, action: #selector(selectHeaderView)))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder : aDecoder)
self.addGestureRecognizer(UITapGestureRecognizer(target : self, action: #selector(selectHeaderView)))
}
func selectHeaderView(gesture : UITapGestureRecognizer) {
let cell = gesture.view as! HeaderView
delegate?.toggleHeader(header: self, section: cell.section)
}
func customInit(titleLabel : String, section : Int, delegate : HeaderViewDelegate) {
self.stateNameLabel.text = titleLabel
self.section = section
self.delegate = delegate
}
#IBAction func selectPrimaryCondition(_ sender: M13Checkbox) {
// get section when favourite state radioButton is selected
delegateHeader?.didTapButton(in: sectionNumber, headerView : self)
}
override func prepareForReuse() {
// What do do here…??
}
}
Here is my ViewController class:
func numberOfSections(in tableView: UITableView) -> Int {
return states.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return states[section].cities.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50.0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (states[indexPath.section].expanded) {
return 44
}else{
return 0.0
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerviewcell") as! HeaderView
var list = states[section]
headerCell.customInit(titleLabel: list.stateName, section: section, delegate: self)
return headerCell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "subcells") as! CollapsibleCell
cell.selectionStyle = .none
cell.textLabel?.text = states[indexPath.section].cities[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// works for headers or cell??
}
func toggleHeader(header : HeaderView, section : Int){
states[section].expanded = !states[section].expanded
for i in 0 ..< states[section].cites.count {
tableView.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
}
}
extension ViewController: HeaderDelegate {
func didTapButton(in section: Int, headerView : HeaderView) {
print("\(section)")
}
}
Expected Output:
What I am getting:
I have Employee.swift which contains the following code:
import Foundation
struct Employee {
var name: String
var favoriteLinks: [String]
var links: [String]
init(name: String, favoriteLinks: [String], links: [String]) {
self.name = name
self.favoriteLinks = favoriteLinks
self.links = links
}
}
And I have ViewController.swift that uses TableView with the following code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var lists: [Employee] = [People(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["www.facebook.com","www.twitter.com"])
]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = lists[indexPath.row].name
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showLinks" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destination = segue.destination as? SecondTableViewController
destination?.talks = lists[indexPath.row].talk
destination?.links = lists[indexPath.row].link
}
}
}
}
And another TableViewController that contains the following code:
import UIKit
class SecondTableViewController: UITableViewController {
var favoriteLinks: [String] = []
var links: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favoriteLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = favoriteLinks[indexPath.row]
return cell
}
}
I created a property that contain name of an employee with a lists of his favoriteLinks and links. ViewController contain a tableview that should only contain the name of the employee, if clicked on the employee, you will be redirected to another tableview with a lists of his favoriteLists.
This is the problem. Because the tableview is only showing text and not the link. I would like the text to contain the link as well, in which if clicked upon would direct you to the link connected. For example, if clicked on Facebook, it will show me to www.facebook.com. What would be the best way to go around to achieve this?
I tried creating two separate arrays to contain the information but I have no idea how to call the array that contains the link. Any help would be appreciated. Thank you!
You have to add didSelectRowAt in SecondTableViewController like below:
if your lists is like below :
var lists: [Employee] = [Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["http://www.facebook.com","http://www.twitter.com"])
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIApplication.shared.open(URL(string: links[indexPath.row])!, options: [:])
}
if your lists is like below :
var lists: [Employee] = [Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["www.facebook.com","www.twitter.com"])
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var urlStr: String = links[indexPath.row]
if !urlStr.hasPrefix("http") && !urlStr.hasPrefix("https") {
urlStr = "http://\(urlStr)"
}
UIApplication.shared.open(URL(string: urlStr)!, options: [:])
}
Better yet, pass the whole Employee record to your second view controller:
class ViewController: UITableViewController {
// Add the https protocol for the link
var lists: [Employee] = [
Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["https://www.facebook.com","https://www.twitter.com"])
]
...
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showLinks" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destination = segue.destination as! SecondTableViewController
// Pass the whole employee record to the second view controller
destination.employee = lists[indexPath.row]
}
}
}
}
And the second view controller:
class SecondTableViewController: UITableViewController {
var employee: Employee!
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return employee.favoriteLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Select the prototype cell in IB and change its style to Subtitle if you want a 2-line label
// Alternatively you can hide the link altogether, it doesn't affect the outcome
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// For the first line, we display the name of the link
cell.textLabel?.text = employee.favoriteLinks[indexPath.row]
// For the second line, we display the address of the link
cell.detailTextLabel?.text = employee.links[indexPath.row]
cell.detailTextLabel?.textColor = .blue
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let href = employee.links[indexPath.row]
let url = URL(string: href)!
// Open the URL in whatever app that registered to open it. That could be
// the Facebook app or the Twitter app instead of Safari. After it has been
// opened, de-highlight the cell
UIApplication.shared.open(url, options: [:], completionHandler: { _ in tableView.selectRow(at: nil, animated: false, scrollPosition: .none) })
}
}