SWIFT 4 Detail Disclosure - ios

I'm new to programming and have just started with a deep dive into SWIFT and iOS development.
I've been stuck on a problem for over a few hours now and would be much obliged if someone could answer it. The assignment is bucket list and we have to built a basic app that uses two Table views (one dynamic and one static) to display a list of "bucket list" items using navigator controllers and modal display. In our first assignment, we were allowed to use two segues for the edit and save functionality but in the new assignment, we are being asked to refactor and use one segue with the hint being that we should use sender information. The present code uses two segues as I have not figured out how to refactor. As a note, we are using the accessory Detail Disclosure to bring up list items for edit. This is where I am getting stuck. From what I've been able to read, I understand the Disclosure Detail button is not a "true button" rather it is part of the index path. While I think I understand what needs to be done from a conceptual level, I am having a hell of a time actually solving the problem. Thanks in advance.
import UIKit
class BucketListViewController: UITableViewController,
AddItemTableViewControllerDelegate {
var items = ["awesome","lessawesome","great"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected")
}
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
performSegue(withIdentifier: "EditItemSegue", sender: indexPath)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
items.remove(at: indexPath.row)
tableView.reloadData()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddItemSegue"{
let navigationController = segue.destination as! UINavigationController
let addItemTableViewController = navigationController.topViewController as! AddItemTableViewController
addItemTableViewController.delegate = self
}else if segue.identifier == "EditItemSegue"{
let navigationController = segue.destination as! UINavigationController
let AddItemTableViewController = navigationController.topViewController as! AddItemTableViewController
AddItemTableViewController.delegate = self
let indexPath = sender as! NSIndexPath
let item = items[indexPath.row]
AddItemTableViewController.item = item
AddItemTableViewController.indexPath = indexPath
}
}
func cancelButtonPressed(by controller: AddItemTableViewController) {
dismiss(animated: true, completion: nil)
}
func itemSaved(by controller: AddItemTableViewController, with text:String, at indexPath: NSIndexPath?) {
if let ip = indexPath {
items[ip.row] = text
}else{
items.append(text)
}
tableView.reloadData()
dismiss(animated: true, completion: nil)
}
}
AddItemTableViewControllerDelegate
import Foundation
import UIKit
protocol AddItemTableViewControllerDelegate: class {
func itemSaved(by controller: AddItemTableViewController, with text:String, at indexPath: NSIndexPath?)
func cancelButtonPressed(by controller: AddItemTableViewController)
}
AddItemTableViewController
import UIKit
class AddItemTableViewController: UITableViewController {
weak var delegate: AddItemTableViewControllerDelegate?
var item: String?
var indexPath: NSIndexPath?
#IBOutlet weak var itemTextField: UITextField!
#IBAction func cancelButtonPressed(_ sender: UIBarButtonItem) {
delegate?.cancelButtonPressed(by: self)
}
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
let text = itemTextField.text!
delegate?.itemSaved(by: self, with: text, at: indexPath)
}
override func viewDidLoad() {
super.viewDidLoad()
itemTextField.text = item
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Related

Editing table row cell swift 3

I am trying to edit the table row cell. I have different tableViewController set up to display/edit the current events when they touch on the displayed events from initial tableViewController. Whenever I edit an event and hit save, it goes back to the main viewController where i am initially displaying my events, but it doesn't display the edited events. it keeps showing the original events.
Here is the portion of my main tableView file where i am displaying my events:
class EventsTable: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableData = ViewController()
#IBOutlet weak var table: UITableView!
#IBAction func saveToMainViewController (change:UIStoryboardSegue) {
let editViewController = change.source as! EditEventsTableViewController
let index = editViewController.index
let titleString = editViewController.editedTitle
tableData.eventsArray[index!].title = titleString
table.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return tableData.eventsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomeCell
cell.eventTitle.text = tableData.eventsArray[indexPath.row].title
cell.eventLocation.text = tableData.eventsArray[indexPath.row].location
cell.eventDateAndTime.text = tableData.eventsArray[indexPath.row].date
return cell
}
//function to delete cell and saves it
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableData.eventsArray.remove(at: indexPath.row)
table.reloadData()
let savedEvents = NSKeyedArchiver.archivedData(withRootObject: tableData.eventsArray)
UserDefaults.standard.set(savedEvents, forKey: "savedEvents")
UserDefaults.standard.synchronize()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "edit" {
var path = table.indexPathForSelectedRow
let detailViewController = segue.destination as! EditEventsTableViewController
detailViewController.index = path?.row
detailViewController.eventsArray = tableData.eventsArray
}
}
Here is the other tableViewController where i edit my data
class EditEventsTableViewController: UITableViewController {
#IBOutlet weak var txtEditTitle: UITextField!
var index:Int!
var eventsArray = [Event]()
var editedTitle: String!
override func viewDidLoad() {
super.viewDidLoad()
txtEditTitle.text = eventsArray[index!].title
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 0 && indexPath.row == 0 {
txtEditTitle.becomeFirstResponder()
}
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "save" {
editedTitle = txtEditTitle.text
}
}
}
incase you guys need a better look at the project,
download link for the project
Probably in EditEventsTableViewController you missed table view reloading?
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
txtEditTitle.text = eventsArray[index!].title
}
It's unclear, where do you provide data for EditEventsTableViewController. Is it static table view, designed in storyboard?

View doesn't get updated until the second time loaded

I have a main view which is a table view with a list of countries. When clicking on any country name (cell), another view is loaded via segue which is passing the name of the country to the next view controller's navigation bar title.
The problem is on the first click the title isn't updated, but when I click back button (dismissing the current view) and click on another country name, the second view loads again and shows the previous title that was suppose to be shown on the first attempt.
The code for the first main view controller:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var sectionsArray = [String]()
var sectionsCountries = [Array<AnyObject>]()
#IBOutlet weak var countries: UITableView!
internal func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections.
return self.sectionsArray.count
}
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return self.sectionsCountries[section].count
}
internal func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sectionsArray[section]
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CountryCell", for: indexPath)
cell.textLabel?.text = self.sectionsCountries[indexPath.section][indexPath.row] as? String
return cell
}
var valueToPass:String!
internal func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as UITableViewCell!;
valueToPass = currentCell?.textLabel?.text
performSegue(withIdentifier: "cellSegue", sender: self)
//print(valueToPass)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellSegue" {
let destination = segue.destination as! CountryViewController
destination.passedValue = valueToPass
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "http://cyber7.co.il/swift/countries/countries-list.json")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers)
for result in jsonResult as! [Dictionary<String, AnyObject>]{
self.sectionsArray.append(result["sectionName"] as! String)
self.sectionsCountries.append(result["sectionCountries"] as! Array<String> as [AnyObject])
}
} catch {
print("JSON Processing Failed")
}
DispatchQueue.main.async(execute: { () -> Void in
self.countries.reloadData()
})
}
}
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
the code for the second view controller:
import UIKit
class CountryViewController: UIViewController {
var passedValue:String!
#IBOutlet weak var navBar: UINavigationBar!
#IBAction func backButton(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
self.navBar.topItem?.title = passedValue
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When you have a segue set up from a table view cell to a view controller in the storyboard then it is performed automatically when a cell is selected. Your call to perform a segue in your cell selection method is performing the segue a second time after the first one has already been performed.
Remove your tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) method and do all the data passing logic in prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellSegue" {
let destination = segue.destination as! CountryViewController
let indexPath = countries.indexPathForSelectedRow
let currentCell = countries.cellForRow(at: indexPath!)
destination.passedValue = currentCell?.textLabel?.text
}
}

UiTableView to UiTableView to UiViewcontroller swift

I am new here and to coding and I need some help with a project that I'm currently stuck on.
I have built an app containing UITableView the first displays body areas (Gastro, cardio, respiratory, etc), the second displays more detailed features on the first (in gastro the table will display stomach, small intestine, colon, etc) and then the the UIViewController will have the more detailed info on the stomach etc.
I have this working all ok but one problem has arose. The UIViewController, regardless of which option is selected in the first table, displays the same info, i.e I get results for gastro even after selecting cardio etc.
Below is my code. I hope someone could help with this. I would really appreciate it!
import UIKit
class Chapters: UITableViewController {
var ChaptersArray = [String]()
var SubchaptersArray = [secondTable]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// chapter names
ChaptersArray = ["Gastrointestinal","Cardiovascular","Respiratory","Central Nervous System", "Infections"]
// subcharpter names
SubchaptersArray = [secondTable(SecondTitle: ["Gas1", "Gas2", "Gas3", "Gas4"]),
secondTable(SecondTitle: ["Card1", "Card2", "Card3", "Card4"]),
secondTable(SecondTitle: ["Res1", "Res2", "Res3", "Res4"])]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableview: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell!
cell.textLabel?.text = ChaptersArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ChaptersArray.count
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow!
let destViewController = segue.destinationViewController as! Subchapters
var secondTableArrayTwo : secondTable
secondTableArrayTwo = SubchaptersArray[indexPath.row]
destViewController.SubChaptersArray = secondTableArrayTwo.SecondTitle
}
}
import UIKit
class Subchapters: UITableViewController {
var SubChaptersArray = [String]()
var identities = [String]()
var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
identities = ["G1","G2","G3","G4"]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return SubChaptersArray.count
}
override func tableView(tableview: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCellWithIdentifier("SecondCell") as UITableViewCell!
cell.textLabel?.text = SubChaptersArray[indexPath.row]
return cell
}
// ignore
// override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// let destViewController = segue.destinationViewController as! DrugNotes
// destViewController.FirstString = "drug data: need to seperate out to seperate pages" }
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let vcName = identities[indexPath.row]
let viewcontroller = storyboard?.instantiateViewControllerWithIdentifier(vcName)
self.navigationController?.pushViewController(viewcontroller!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

How to create Dynamic Table View cells With multiple View Controller in swift 2.0?

Hi all. I'm troubling with tableView in swift. Actually i created the table view with two rows(About and Login) in main view controller. Problem at initial, when i click the about or Login , then the New View controller is not opened. But, I try second time to another one row, then the first clicked view Controller is opened at this time of Clicking. so, this cycles shown wrong view controller at every time of clicking. Please tell me as whats wrong with my Code?? Please refer screenshot given below.Thanks in advance!!!
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var titles = ["About","LogIn"]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as! TableCell
cell.label.text = titles[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
switch indexPath.row
{
case 0: self.performSegueWithIdentifier("aboutSegue", sender: self)
case 1: self.performSegueWithIdentifier("loginSegue", sender: self)
default: break
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "aboutSegue"
{
let vc = segue.destinationViewController as! about
vc.title = "About"
}
else
{
let vc = segue.destinationViewController as! login
vc.title = "Login"
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Below Screen shown, I clicked About Row. But, the Login ViewController is Opened.
This is my Story Board Connections.
Just update didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
switch indexPath.row
{
case 0:
self.performSegueWithIdentifier("aboutSegue", sender: self)
break;
case 1:
self.performSegueWithIdentifier("loginSegue", sender: self)
break;
default: break
}
}

Swift: Passing data from a tableView in a ViewController to another ViewController

I'm trying to pass the indexPath.row data from the selected cell to the next view controller using both the didSelectCellAtIndexPath and the prepareForSegue methods but the value isnt passing through.
Code for both ViewControllers is show below:
import UIKit
class MainMenu: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
// array of the menu options
let mainMenuOptions = ["Exercises 1", "Exercises 2", "Exercises 3"]
// UITableView
#IBOutlet weak var exerciseOptions: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
exerciseOptions.delegate = self
exerciseOptions.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Table configuration
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "ExercisesTableViewCells"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExercisesTableViewCell
cell.textLabel!.text = mainMenuOptions[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
// Segue to VC based on row selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0{
self.performSegueWithIdentifier("SegueToExercises", sender: self)
}
else if indexPath.row == 1{
self.performSegueWithIdentifier("SegueToExercises", sender: self)
}
else{
self.performSegueWithIdentifier("SegueToReminders", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "SegueToExercise"){
let viewController = segue.destinationViewController as? ExerciseMenu
viewController!.mainMenuValue = exerciseOptions.indexPathForSelectedRow!.row
}
}
For the Second View Controller:
import UIKit
class ExerciseMenu: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var exerciseOptionsTitle: UILabel!
#IBOutlet weak var exerciseOptionsTable: UITableView!
#IBAction func beginWorkout(sender: AnyObject) {
}
// receives data from MainMenu
var mainMenuValue: Int!
override func viewDidLoad() {
super.viewDidLoad()
exerciseOptionsTable.delegate = self
exerciseOptionsTable.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "ExerciseMenuCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExercisesTableViewCell
cell.textLabel!.text = String(mainMenuValue)
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
See this thread for possible explanations for this behavior.
The first step of troubleshooting would be to put a breakpoint in your prepareForSegue method to see if exerciseOptions.indexPathForSelectedRow is already nil at that point, in which case something is happening before the segue even occurs that's messing with the value (which the link above can help you diagnose).
Try making a property in your table view controller called something like selectedRow, and in didSelectRowAtIndexPath, set self.selectedRow = indexPath.row. This way you have your own variable that'll be more reliable than exerciseOptions.indexPathForSelectedRow. Then change the code in prepareForSegue appropriately. So to give you the full methods as I'm envisioning them:
(As a property of the first view controller) var selectedRow: Int?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0{
self.performSegueWithIdentifier("SegueToExercises", sender: self)
self.selectedRow = 0
}
else if indexPath.row == 1{
self.performSegueWithIdentifier("SegueToExercises", sender: self)
self.selectedRow = 1
}
else{
self.performSegueWithIdentifier("SegueToReminders", sender: self)
self.selectedRow = indexPath.row
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "SegueToExercise"){
let viewController = segue.destinationViewController as? ExerciseMenu
viewController!.mainMenuValue = self.selectedRow!
}
}
Disclaimer: I'm still learning Swift, so my judgment regarding optionals/optional unwrapping might be unideal for a technical reason I'm not aware of, but the overall theory here is sound, I think.

Resources