I have a view controller which contains a table view. In this view controller, I've also set up a floating panel. The floating panel is not appearing for some reason, however. I know the ContentViewController is working, so I didn't include that code. The error is somewhere in this code and I can't find it. It's like the FloatingPanel is just not being added to the view.
import Foundation
import UIKit
import FloatingPanel
class Section {
let title: String
let options: [String]
var isOpened: Bool = false
init(title: String,
options: [String],
isOpened: Bool
) {
self.title = title
self.options = options
self.isOpened = isOpened
}
}
class PicsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UINavigationControllerDelegate, FloatingPanelControllerDelegate {
#IBOutlet weak var addButton: UIButton!
private let picsTableView: UITableView = {
let picsTableView = UITableView()
picsTableView.register(UITableViewCell.self,
forCellReuseIdentifier: "cell")
return picsTableView
}()
private var sections = [Section]()
let backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
/*------------START FLOATING PANEL-------------*/
var fpc: FloatingPanelController!
override func viewDidLoad() {
super.viewDidLoad()
print("PicsViewController LAUNCHED!!")
fpc = FloatingPanelController()
fpc.delegate = self
// guard let contentVC = storyboard?.instantiateViewController(identifier: "fpc_content") as? ContentViewController else {
// print("Failed to instantiate storyboard")
// return
// }
let contentVC = ContentViewController()
fpc.set(contentViewController: contentVC)
// fpc.track(scrollView: contentVC.collectionView)
fpc.addPanel(toParent: self)
/*------------END FLOATING PANEL-------------*/
// Set up models
picsTableView.delegate = self
picsTableView.dataSource = self
picsTableView.frame = view.bounds
picsTableView.backgroundColor = backgroundColor
view.addSubview(picsTableView)
sections = [
Section(title: "Section 1", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 2", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 3", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 4", options: ["Test 1", "Test 2", "Test 3"], isOpened: false)
]
// get rid of extra table view cells
picsTableView.tableFooterView = UIView()
// customize button
addButton.layer.cornerRadius = 0.5 * addButton.bounds.size.width
addButton.clipsToBounds = true
addButton.backgroundColor = .red
addButton.tintColor = .white
addButton.setImage(#imageLiteral(resourceName: "plus-5-512"), for: .normal)
view.bringSubviewToFront(addButton)
}
#IBAction func addButtonTapped(_ sender: Any) {
print("Add button tapped")
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
if section.isOpened {
return section.options.count + 1
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = picsTableView.dequeueReusableCell(
withIdentifier: "cell",
for: indexPath
)
cell.backgroundColor = .clear
cell.textLabel?.textColor = .black
if indexPath.row == 0 {
cell.textLabel?.text = sections[indexPath.section].title
} else {
cell.textLabel?.text = sections[indexPath.section].options[indexPath.row - 1]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
picsTableView.deselectRow(at: indexPath, animated: true)
if indexPath.row == 0 {
sections[indexPath.section].isOpened = !sections[indexPath.section].isOpened
picsTableView.reloadSections([indexPath.section], with: .none)
} else {
print("Tapped sub cell \(indexPath.row)")
}
}
}
Typically, 3 or 4 statements to add any view controller as subview to any view controllers are
let viewController = SomeViewController()
self.addChild(viewController)
self.view.addSubview(viewController.view)
viewController.didMove(toParent: self)
Because you havent provided the implementation for addPanel where you are calling fpc.addPanel(toParent: self), I assume it does something similar to what I have written above, if I were to write it, I might write something like
func addPanel(toParent: UIViewController) {
toParent.addChild(self)
toParent.view.addSubview(self.view)
//add constraints or set frames for your subview
self.didMove(toParent: toParent)
}
Finally, you are adding multiple view's to your view controller's view namely a floating panel, tableView, and a button. Because you add panel before you add all other views it might be hidden under other subviews like tableView, you can obviously check this hypothesis using view debugger, and simplest way to fix it would be to use bringSubviewToFront, but issue is you are not holding the reference of FloatingPanelController's view so you can try adding self.view.bringSubviewToFront(fpc.view) as last statement of ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// all other codes of yours
self.view.bringSubviewToFront(fpc.view)
}
and for some reason if that doesnt work call fpc.addPanel(toParent: self) as last statement of ViewDidLoad instead of adding in between and add bringSubviewToFront as last statement of addPanel(toParent method
override func viewDidLoad() {
super.viewDidLoad()
print("PicsViewController LAUNCHED!!")
fpc = FloatingPanelController()
fpc.delegate = self
let contentVC = ContentViewController()
fpc.set(contentViewController: contentVC)
picsTableView.delegate = self
picsTableView.dataSource = self
picsTableView.frame = view.bounds
picsTableView.backgroundColor = backgroundColor
view.addSubview(picsTableView)
sections = [
Section(title: "Section 1", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 2", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 3", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
Section(title: "Section 4", options: ["Test 1", "Test 2", "Test 3"], isOpened: false)
]
// get rid of extra table view cells
picsTableView.tableFooterView = UIView()
// customize button
addButton.layer.cornerRadius = 0.5 * addButton.bounds.size.width
addButton.clipsToBounds = true
addButton.backgroundColor = .red
addButton.tintColor = .white
addButton.setImage(#imageLiteral(resourceName: "plus-5-512"), for: .normal)
view.bringSubviewToFront(addButton)
fpc.addPanel(toParent: self) //added as last statement
}
and
func addPanel(toParent: UIViewController) {
toParent.addChild(self)
toParent.view.addSubview(self.view)
self.didMove(toParent: toParent)
toParent.view.bringSubviewToFront(self.view)
}
Related
Such a beginner question but I am getting this error and I have tried to rearrange the braces in a million different ways but still return this error one way or another. I have managed to arrange the braces in a fashion to return only one error in one place. Here is a picture of the location of the error as well as the code.
//
// ViewController.swift
// TwoCVs
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class FirstCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var buttonOne: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
func commonInit() {
guard buttonOne != nil else { return }
buttonOne.titleLabel!.font = UIFont(name: "Marker Felt", size: 20)
buttonOne.layer.cornerRadius = 10
buttonOne.clipsToBounds = true
buttonOne.layer.borderWidth = 1.0
buttonOne.layer.borderColor = UIColor.white.cgColor
}
}
class SecondCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var buttonTwo: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
func commonInit() {
guard buttonTwo != nil else { return }
buttonTwo.titleLabel!.font = UIFont(name: "Marker Felt", size: 20)
buttonTwo.layer.cornerRadius = 10
buttonTwo.clipsToBounds = true
buttonTwo.layer.borderWidth = 1.0
buttonTwo.layer.borderColor = UIColor.white.cgColor
}
}
class ThirdCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var buttonThree: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
func commonInit() {
guard buttonThree != nil else { return }
buttonThree.titleLabel!.font = UIFont(name: "Marker Felt", size: 20)
buttonThree.layer.cornerRadius = 10
buttonThree.clipsToBounds = true
buttonThree.layer.borderWidth = 1.0
buttonThree.layer.borderColor = UIColor.white.cgColor
}
}
class TwoCollectionsViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var firstCV: UICollectionView!
#IBOutlet weak var secondCV: UICollectionView!
#IBOutlet weak var thirdCV: UICollectionView!
#IBAction func Buttons(_ sender: Any) {
if let btn = sender as? UIButton {
print(btn.restorationIdentifier!)
guard let button = sender as? UIButton else { return }
guard let id = Int(button.restorationIdentifier!) else {return}
if id == 0 {
performSegue(withIdentifier: "Good Work", sender: btn)
}
else if id == 1 {
performSegue(withIdentifier: "Nice Try", sender: btn)
}
else if id == 6 {
performSegue(withIdentifier: "Second 1", sender: btn)
}
}
}
let firstData: [String] = [
"Good Work", "Nice Try", "Btn 3", "Btn 4", "Btn 5", "Btn 6"
]
let secondData: [String] = [
"Second 1", "Second 2", "Second 3", "Second 4", "Second 5", "Second 6", "Second 7"
]
let thirdData: [String] = [
"Action 1", "Action 2", "Action 3", "Action 4", "Action 5", "Action 6"]
override func viewDidLoad() {
super.viewDidLoad()
firstCV.dataSource = self
firstCV.delegate = self
secondCV.dataSource = self
secondCV.delegate = self
thirdCV.dataSource = self
thirdCV.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// if it's the First Collection View
if collectionView == firstCV {
return firstData.count
// it's not the First Collection View, so it's the Second one
return secondData.count
}
return thirdData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == firstCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
cell.buttonOne.setTitle(firstData[indexPath.item], for: []) //allows for button title change in code above
cell.buttonOne.restorationIdentifier = "\(indexPath.row)"
return cell
}
// it's not the First Collection View, so it's the Second one
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
cell.buttonTwo.setTitle(secondData[indexPath.item], for: [])
cell.buttonTwo.restorationIdentifier = "\(indexPath.row + firstData.count)"
return cell
}
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "thirdCell", for: indexPath) as! ThirdCollectionViewCell
cell.buttonThree.setTitle(thirdData[indexPath.item], for: [])
cell.buttonThree.restorationIdentifier = "\(indexPath.row + secondData.count)"
return cell
}
}
I guess maybe you want your last method to be like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == firstCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
cell.buttonOne.setTitle(firstData[indexPath.item], for: []) //allows for button title change in code above
cell.buttonOne.restorationIdentifier = "\(indexPath.row)"
return cell
} else if collectionView == secondCV {
// it's not the First Collection View, so it's the Second one
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
cell.buttonTwo.setTitle(secondData[indexPath.item], for: [])
cell.buttonTwo.restorationIdentifier = "\(indexPath.row + firstData.count)"
return cell
} else if collectionView == thirdCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "thirdCell", for: indexPath) as! ThirdCollectionViewCell
cell.buttonThree.setTitle(thirdData[indexPath.item], for: [])
cell.buttonThree.restorationIdentifier = "\(indexPath.row + secondData.count)"
return cell
}
}
I am making a screen for a program schedule for two days. I’ve got a ViewController with the following layout:
NavigationBar - SearchBar - Segmented control - TableView.
In a separate file UITableViewCell I draw a custom cell. The main logic in my VC:
struct Schedule {
var time: String
var title: String
}
struct SectionForDay {
let sectionTitle: String
var dayProgram: [Schedule]
}
class ProgramViewController: UIViewController {
var tableView = UITableView()
let identifier = "Cell"
var dayOne = [
Schedule(time: "10:00 - 11:00", title: "DayOne SessionOne"),
Schedule(time: "11:00 - 12:00", title: "DayOne SessionTwo")
]
var dayTwo = [
Schedule(time: "22:00 - 23:00", title: "DayTwo SessionThree"),
Schedule(time: "24:00 - 01:00", title: "DayTwo SessionFour")
]
var sections = [SectionForDay]()
let segmentedControl: UISegmentedControl = {
let sc = UISegmentedControl(items: ["All", "Day 1", "Day 2"])
sc.selectedSegmentIndex = 0
sc.addTarget(self, action: #selector(handleSegmentedChange), for: .valueChanged)
return sc
}()
#objc func handleSegmentedChange() {
switch segmentedControl.selectedSegmentIndex {
case 0:
dayToDisplay = dayOne + dayTwo
case 1:
dayToDisplay = dayOne
default:
dayToDisplay = dayTwo
}
tableView.reloadData()
}
lazy var dayToDisplay = dayOne + dayTwo
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(ProgramCell.self, forCellReuseIdentifier: identifier)
sections = [
SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo)
]
}
extension ProgramViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section].sectionTitle
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let items = self.sections[section].dayProgram
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! ProgramCell
let items = self.sections[indexPath.section].dayProgram
let currentDay = items[indexPath.row]
cell.dateLabel.text = currentDay.time
cell.titleLabel.text = currentDay.title
return cell
}
}
I tried several methods, but still can’t make the segmented control switch the way, so that in All it shows both two days with their section headers, Day 1 - only day one program with its section header, Day 2 - only day two program with its section header. Can anybody give me a hint of what to do? Maybe I should change the whole model?
Image:
When I toggle the segmented control between 3 items it always shows two days.
You need to update your sections array when the segmented control value changes.
#objc func handleSegmentedChange() {
switch segmentedControl.selectedSegmentIndex {
case 0:
sections = [
SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo),
]
case 1:
sections = [
SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
]
default:
sections = [
SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo),
]
}
tableView.reloadData()
}
How would I create a new view that displays unique information for the selection of a specific tableview cell? For example, if row 1 is selected then the new image will have unique information to that cell which would be different from the view if row 2 was selected.
Here is the code for my tableview.
import UIKit
struct Data {
var sectionTitle = String()
var rowTitles = [String]()
}
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var dataArray = [Data(sectionTitle: "section 1", rowTitles: ["row 1", "row 2", "row 3"]),
Data(sectionTitle: "section 2", rowTitles: ["row 1"]),
Data(sectionTitle: "section 3", rowTitles: ["row 1", "row 2"])
]
var searchArray = [Data]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchArray[section].rowTitles.count
} else {
return dataArray[section].rowTitles.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell")
cell?.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell?.textLabel?.numberOfLines = 3
if searching {
cell?.textLabel?.text = self.searchArray[indexPath.section].rowTitles[indexPath.row]
} else {
cell?.textLabel?.text = self.dataArray[indexPath.section].rowTitles[indexPath.row]
}
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
if searching {
return searchArray.count
} else {
return dataArray.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dataArray[section].sectionTitle
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = indexPath.row
let data = dataArray[index]
let destVC = SecondViewController(data: data)
navigationController?.pushViewController(destVC, animated: true)
}
}
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
view.endEditing(true)
tableView.reloadData()
} else {
searching = true
searchArray = dataArray.filter({$0.sectionTitle.lowercased().contains(searchBar.text!.lowercased()) || $0.rowTitles.map{$0.lowercased()}.contains(searchBar.text!.lowercased())})
tableView.reloadData()
}
}
}
Here is my second view controller which shows my generic stack view that I would like to change to display different images and labels depending on the cell that is selected in the tableview.
import UIKit
class SecondViewController: UIViewController {
var data: Data!
var dataView: DataView { return self.view as! DataView}
init(data: Data) {
self.data = data
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
self.view = DataView(frame: UIScreen.main.bounds)
}
}
class DataView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
self.addSubview(stack)
stack.distribution = .fillProportionally
stack.spacing = 10
}
func setupConstraints() {
stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
dataImage.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
dataImage.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 60).isActive = true
dataImage.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -60).isActive = true
}
let dataNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 20, weight: .bold)
label.textAlignment = .center
label.numberOfLines = 2
label.text = "Organization"
return label
} ()
let dataDescriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
label.textAlignment = .center
label.numberOfLines = 8
label.text = "Description"
return label
} ()
let dataImage: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "")?.withRenderingMode(.alwaysOriginal)
imageView.backgroundColor = .gray
imageView.layer.cornerRadius = 8
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 260).isActive = true
return imageView
} ()
let visitButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Visit", for: .normal)
return button
} ()
lazy var stack: UIStackView = {
let stack = UIStackView(arrangedSubviews: [dataImage, dataNameLabel, dataDescriptionLabel, visitButton])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
return stack
} ()
}
The objective is to have a user select a tableview cell, and for the stack view to display specific information that corresponds to that cell. How would I create this?
I'm not sure if you're asking how to pass information to a new view controller when an item in your TableViewController has been selected.
But I'm interpreting your question, as you know how to pass information, but you want to pass different information, maybe an image to your second view controller, based on which TableViewCell was selected.
The way that I would do this, is to have an array with images in your SecondViewController. When an item is selected in your TableViewController, also pass the indexPath.row to the SecondViewController. From there, you can load the image from your imagesArray at the indexPath.row that you have passed to it. You can do this by having an integer value in your SecondViewController, that is populated right before you segue.
i am new to swift and now i'm trying to create a dropdown table on storyboard using cocoapods. i have followed all the tutorial from the cocoapods & i guess there is something wrong with my code here.
Please give me an enlightened here why it won't work where i have declared the dropdown.
Thankyou for helping, appreciate it guys.
import iOSDropDown
class ViewController: UIViewController {
#IBOutlet weak var DropDownMenu: DropDown!
let dropDown = DropDown()
let view = UIView()
dropDown.anchorView = view
dropDown.optionArray = ["option 1", "option 2", "option 3"]
override func viewDidLoad() {
super.viewDidLoad()
let dropDown = DropDown()
let view = UIView()
dropDown.anchorView = view // UIView or UIButton
// For set direction .top, .bottom or .any(for automatic)
dropDown.direction = .any
dropDown.optionArray = ["data 1", "data 2", "data 3"]
// for custom cell you can set xib
dropDown.cellNib = UINib(nibName: "MyCell", bundle: nil)
// select item from dropdown
dropDown.selectionAction = { [unowned self] (index: Int, item: String) in
print("Selected item: \(item) at index: \(index)")
}
// if you want to set custom width then you can set.
dropDownLeft.width = 200
dropDown.show() // For Display Dropdown
dropDown.hide() // For hide Dropdown
}
for more detail and features you can refer https://github.com/AssistoLab/DropDown
import iOSDropDown
class ViewController: UIViewController,UITextFieldDelegate {
#IBOutlet weak var dropDown: DropDown!
override func viewDidLoad() {
super.viewDidLoad()
// The list of array to display. Can be changed dynamically
dropDown.optionArray = ["Option 1", "Option 2", "Option 3"]
//Its Id Values and its optional
dropDown.optionIds = [1,23,54,22]
// The the Closure returns Selected Index and String
dropDown.didSelect{(selectedText , index ,id) in
print("Selected String: \(selectedText) \n index: \(index)")
self.dropDown.hideList()
self.dropDown.text = selectedText
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
dropDown.showList()
}
}
you can refer this link https://cocoapods.org/pods/iOSDropDown
Step #1 : Add Pod and install
pod 'DropDown'
Step #2: Adding a drop-down in UIButton Tap
import UIKit
import DropDown
class ViewController: UIViewController {
let dropDown = DropDown()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func tapChooseItems(_ sender: UIButton) {
dropDown.dataSource = ["option 1", "option 2", "option 3"]
dropDown.anchorView = sender
dropDown.bottomOffset = CGPoint(x: 0, y: sender.frame.size.height)
dropDown.show()
dropDown.selectionAction = { [weak self] (index: Int, item: String) in
guard let _ = self else { return }
sender.setTitle(item, for: .normal)
}
}
}
Step #3: Adding a drop-down inside UITableView
import UIKit
import DropDown
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let dropDown = DropDown()
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Result: \(indexPath.row+1): "
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
dropDown.dataSource = ["option 1", "option 2", "option 3"]
dropDown.anchorView = cell
dropDown.bottomOffset = CGPoint(x: 0, y: cell.frame.size.height)
dropDown.backgroundColor = .gray
dropDown.show()
dropDown.selectionAction = { [weak self] (index: Int, item: String) in
guard let _ = self else { return }
cell.textLabel?.text = "Result: \(indexPath.row+1): \(item)"
}
}
}
}
Write the initialization code in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
let dropDown = DropDown()
let view = UIView()
dropDown.anchorView = view
dropDown.optionArray = ["option 1", "option 2", "option 3"]
}
I have a table view controller that displays names of shopping lists. These shopping lists are created through a shopping list class, which lets me specify the name of the shopping list and items in it. This table view displays around 5 shopping lists (names). When I press one, I go to another table view controller, which shows the items of the shopping list. However, when I go back to the shopping list (names) table view controller and press another shopping list, it still displays the items from the old shopping list and not the one that was pressed. I think I have narrowed the problem down to the cellForItemAt of the second table view controller not being called again.
import UIKit
class List: NSObject {
var name: String
var items: [String?]
var shared: String?
init(name: String, items: [String], shared: String?) {
self.name = name
self.items = items
self.shared = shared
}
}
enum ListType {
case apply
case view
}
class ListViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId"
var lists: [List] = [
List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
]
//Create Class Lazy Vars
lazy var newListVC: NewListViewController = {
let launcher = NewListViewController()
// launcher.homeController = self
return launcher
}()
lazy var listItemsVC: ListItemsViewController = {
let launcher = ListItemsViewController()
// launcher.homeController = self
return launcher
}()
//Setup Enums and Switches
var listType: ListType!
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)
//SWITCH STATEMENT
switch listType as ListType {
case ListType.apply:
//Apply Begin
navigationItem.title = "Apply List"
//Setup Navigation Bar Items
let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)
//Add items to navigation bar
navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
//Apply End
case ListType.view:
//View Begin
//Setup Navigation Bar
navigationItem.title = "My Lists"
//Add items to navigation bar
navigationItem.rightBarButtonItem = addBarButtonItem
navigationItem.leftBarButtonItem = editButtonItem
//View End
}
//SWITCH STATEMENT END
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
#objc func newList() {
newListVC.pageControlFunc(pageView: PageView.name)
navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
listItemsVC.listName = lists[indexPath.row].name
listItemsVC.listItems = lists[indexPath.row].items
print(listItemsVC.listItems)
navigationController?.pushViewController(listItemsVC, animated: true)
}
}
///////////////////////////////////////////////////////////////////////////
SECOND UITABLEVIEW - NOT UPDATING
///////////////////////////////////////////////////////////////////////////
import UIKit
class ListItemsViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId1"
var listName: String?
var listItems: [String?] = []
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
//Set Title
navigationItem.title = listName
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
//Delegate method not called on push to view
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path
return cell
}
}
Thanks in advance!
ViewDidLoadis called once, when the ViewController is first loaded into memory. Since your ListsItemsViewController is a strong property on your ListViewController, and you are navigating to it multiple times, nothing is ever telling it to update its views or reload your tableview because it does not get deallocated(so ViewDidLoad and the TableView delegate/datasource methods do not get called). You also haven't added any custom code telling it to update.
If you want to keep those view controllers as class properties of ListViewController, then you can add tableView.reloadData in the ViewWillAppear method of ListsItemsViewController as shown below.
import UIKit
class List: NSObject {
var name: String
var items: [String?]
var shared: String?
init(name: String, items: [String], shared: String?) {
self.name = name
self.items = items
self.shared = shared
}
}
enum ListType {
case apply
case view
}
class ListViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId"
var lists: [List] = [
List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
]
//Create Class Lazy Vars
lazy var newListVC: NewListViewController = {
let launcher = NewListViewController()
// launcher.homeController = self
return launcher
}()
lazy var listItemsVC: ListItemsViewController = {
let launcher = ListItemsViewController()
// launcher.homeController = self
return launcher
}()
//Setup Enums and Switches
var listType: ListType!
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)
//SWITCH STATEMENT
switch listType as ListType {
case ListType.apply:
//Apply Begin
navigationItem.title = "Apply List"
//Setup Navigation Bar Items
let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)
//Add items to navigation bar
navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
//Apply End
case ListType.view:
//View Begin
//Setup Navigation Bar
navigationItem.title = "My Lists"
//Add items to navigation bar
navigationItem.rightBarButtonItem = addBarButtonItem
navigationItem.leftBarButtonItem = editButtonItem
//View End
}
//SWITCH STATEMENT END
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
#objc func newList() {
newListVC.pageControlFunc(pageView: PageView.name)
navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
listItemsVC.listName = lists[indexPath.row].name
listItemsVC.listItems = lists[indexPath.row].items
print(listItemsVC.listItems)
navigationController?.pushViewController(listItemsVC, animated: true)
}
}
///////////////////////////////////////////////////////////////////////////
SECOND UITABLEVIEW - NOT UPDATING
///////////////////////////////////////////////////////////////////////////
import UIKit
class ListItemsViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId1"
var listName: String?
var listItems: [String?] = []
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
//Set Title
navigationItem.title = listName
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
//Delegate method not called on push to view
}
//This will always get called when this view controller's view is added to the window
//so you can guarantee it will call your delegate methods to reload the tableView with your new data
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated: animated)
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path
return cell
}
}
The other option would be removing those lazy vars and just initializing those view controllers as needed, so that they get deallocated once you remove them from the stack. This would mean that your tableviews would always reload without having to add any code in ViewWillAppear
import UIKit
class List: NSObject {
var name: String
var items: [String?]
var shared: String?
init(name: String, items: [String], shared: String?) {
self.name = name
self.items = items
self.shared = shared
}
}
enum ListType {
case apply
case view
}
class ListViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId"
var lists: [List] = [
List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
]
//Setup Enums and Switches
var listType: ListType!
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)
//SWITCH STATEMENT
switch listType as ListType {
case ListType.apply:
//Apply Begin
navigationItem.title = "Apply List"
//Setup Navigation Bar Items
let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)
//Add items to navigation bar
navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
//Apply End
case ListType.view:
//View Begin
//Setup Navigation Bar
navigationItem.title = "My Lists"
//Add items to navigation bar
navigationItem.rightBarButtonItem = addBarButtonItem
navigationItem.leftBarButtonItem = editButtonItem
//View End
}
//SWITCH STATEMENT END
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
#objc func newList() {
let newListVC = NewListViewController()
newListVC.pageControlFunc(pageView: PageView.name)
navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let listItemsVC = ListItemsViewController()
listItemsVC.listName = lists[indexPath.row].name
listItemsVC.listItems = lists[indexPath.row].items
print(listItemsVC.listItems)
navigationController?.pushViewController(listItemsVC, animated: true)
}
}
///////////////////////////////////////////////////////////////////////////
SECOND UITABLEVIEW - NOT UPDATING
///////////////////////////////////////////////////////////////////////////
import UIKit
class ListItemsViewController: UITableViewController {
//Initialize Variables Here
let cellId = "cellId1"
var listName: String?
var listItems: [String?] = []
override func viewDidLoad() {
super.viewDidLoad()
//Things that both cases have in common:
navigationController?.navigationBar.prefersLargeTitles = true
//Set Title
navigationItem.title = listName
//Register TableView with Id: cellId
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
//Delegate method not called on push to view
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path
return cell
}
}