SWIFT - UITableViewCell updating based on selection - ios

I have a TableViewController (lets call TVC1) with a row that says "OD" (which stands for Outer Diameter).
Upon selecting this row, a bunch of rows in a new TableViewController (lets call TVC2) containing the various OD (casingOD in my code) shows. What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection. My code for this currently fails...Could anyone help point me in the right direction? If you require TVC1 code i'll happily post it, i'm just trying to save any unneccessary code reading for you folks :)
My TVC2 code is as follows:
import UIKit
class CasingSelectionTableViewController: UITableViewController {
var selectedData: Data?
let casingOD = ["114.3", "127.0", "139.7", "168.3" , "177.8", "193.7", "219.1", "244.5", "247.6", "273.1", "298.4", "298.4", "339.7", "406.4", "473.0", "508"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
switch selectedData! {
case .OuterDiameter:
print(casingOD)
case .Weight:
print(casingWeight114) // I deleted the casingWeight114 line of code as its not required for this question
case .InnerDiameter:
print(id114) // I deleted the id114 line as its not required for this question
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return casingOD.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var casingSpec: UITableViewCell!
if selectedData == Data.OuterDiameter {
casingSpec = tableView.dequeueReusableCellWithIdentifier("selectedCasingSpec", forIndexPath: indexPath)
let casingODSpec = casingOD[indexPath.row]
casingSpec.textLabel?.text = casingODSpec
return casingSpec
} else {
return casingSpec
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selection: UITableViewCell!
selection.textLabel?.text = indexPath.row as! String
}

What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection.
First of all you'll need to implement a way for TVC2 to notify TVC1 that a value has been selected.
A common way to do such thing is by using delegation. You can define a delegate protocol like this:
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
Then add a var delegate: TVC2Delegate? property to TVC2.
You'll then make TVC1 comform to TVC2Delegate by implementing that method in TVC1.
When presenting TVC2 from TVC1 remember to set it as the delegate for TVC2.
// In TVC1
tvc2.delegate = self
To connect TVC1 and TVC2 you could add a bit o logic to your tableView(tableView:,didSelectRowAtIndexPath:) method call the delegate with the selected value
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = indexPath.row as! String
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
Finally, in TVC1's implementation of the delegate method you can take care of dismissing TVC2 if needed.
Update:
This is how the final implementation of these bits might look like:
// In TVC1
class TVC1: UITableViewController, TVC2Delegate {
// ...
// Implement the method(s) of TVC2Delegate
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String) {
// Do whatever you need to do with the outerDiameter parameter
}
}
// In TVC2
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
class CasingSelectionTableViewController: UITableViewController {
var delegate: TVC2Delegate?
// ...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = casingOD[indexPath.row]
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
}

Use the delegate approach as suggested in the answer by #Mokagio. And in case you're having issue in getting the string, here is the answer
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UITableViewCell
let stringValue = cell.textLabel.text //You can get this from your datasource as well)
//call the delegate
}

Related

How to reload tableView data after data is passed by a Segue

I have two table views. One which the user clicks on and one where data is displayed. When the user clicks on a cell in the first table view a query is made to my firebase database and the query is stored in an Array. I then pass the data through a segue. I used a property observer so I know that the variable is being set. By using break points I was able to determine that my variable obtains its value right before the cellForRowAtIndexPath method. I need help displaying the data in my table view. I do not know where to reload the data to get the table view to update with my data. I am using Swift.
EDIT 2: I have solved my problem. I will post my first and second table views so that you can see my solution.
FirstTableView
import UIKit
import Firebase
import FirebaseDatabase
class GenreTableViewController: UITableViewController {
let dataBase = FIRDatabase.database()
var genreArray = ["Drama","Classic,Comic/Graphic novel","Crime/Detective","Fable,Fairy tale","Fantasy","Fiction narrative", "Fiction in verse","Folklore","Historical fiction","Horror","Humour","Legend","Magical realism","Metafiction","Mystery","Mythology","Mythopoeia","Realistic fiction","Science fiction","Short story","Suspense/Thriller","Tall tale","Western,Biography","Autobiography","Essay","Narrative nonfiction/Personal narrative","Memoir","Speech","Textbook","Reference book","Self-help book","Journalism", "Religon"]
var ResultArray: [NSObject] = []
var infoArray:[AnyObject] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return genreArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel?.text = genreArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController: ResultTableViewController = segue.destinationViewController as! ResultTableViewController
if segue.identifier == "letsGo" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let tappedItem = self.genreArray[indexPath.row]
DestViewController.someString = tappedItem
}
}
}
}
import UIKit
import Firebase
import FirebaseDatabase
class ResultTableViewController: UITableViewController {
let dataBase = FIRDatabase.database()
var SecondResultArray: [FIRDataSnapshot]! = []
var someString: String?{
didSet {
print("I AM A LARGE TEXT")
print(someString)
}
}
override func viewDidLoad() {
let bookRef = dataBase.reference().child("books")
bookRef.queryOrderedByChild("Genre")
.queryEqualToValue(someString)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {
self.SecondResultArray.append(child as! FIRDataSnapshot)
//print(self.ResultArray)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
})
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return SecondResultArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
// Configure the cell...
let bookSnapShot: FIRDataSnapshot! = self.SecondResultArray[indexPath.row]
let book = bookSnapShot.value as! Dictionary<String, String>
let Author = book["Author"] as String!
let Comment = book["Comment"] as String!
let Genre = book["Genre"] as String!
let User = book["User"] as String!
let title = book["title"] as String!
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
cell.textLabel?.text = "Author: " + Author + "\n" + "Comment: " + Comment + "\n" + "Genre: " + Genre + "\n" + "User: " + User + "\n" + "Title: " + title
let photoUrl = book["bookPhoto"], url = NSURL(string:photoUrl!), data = NSData(contentsOfURL: url!)
cell.imageView?.image = UIImage(data: data!)
return cell
}
}
For better context and troubleshooting here is my current code for the tableView which is supposed to display data:
import UIKit
class ResultTableViewController: UITableViewController {
var SecondResultArray: Array<NSObject> = []{
willSet(newVal){
print("The old value was \(SecondResultArray) and the new value is \(newVal)")
}
didSet(oldVal){
print("The old value was \(oldVal) and the new value is \(SecondResultArray)")
self.tableView.reloadData()
}
}
override func viewDidLoad() {
print ("I have this many elements\(SecondResultArray.count)")
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return SecondResultArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
cell.textLabel?.text = SecondResultArray[indexPath.row] as? String
return cell
}
}
Edit:
Here is my first table view controller. I have tried using the completion handler, but I can't call it correctly and I am constricted by the fact that my query happens in the didSelectRowAtIndexPath method. Please help.
import UIKit
import Firebase
import FirebaseDatabase
class GenreTableViewController: UITableViewController {
let dataBase = FIRDatabase.database()
var genreArray = ["Drama","Classic,Comic/Graphic novel","Crime/Detective","Fable,Fairy tale","Fantasy","Fiction narrative", "Fiction in verse","Folklore","Historical fiction","Horror","Humour","Legend","Magical realism","Metafiction","Mystery","Mythology","Mythopoeia","Realistic fiction","Science fiction","Short story","Suspense/Thriller","Tall tale","Western,Biography","Autobiography","Essay","Narrative nonfiction/Personal narrative","Memoir","Speech","Textbook","Reference book","Self-help book","Journalism", "Religon"]
var ResultArray: [NSObject] = []
var infoArray:[AnyObject] = []
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return genreArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = genreArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
typealias CompletionHandler = (result:NSObject?, error: NSError?) -> Void
func getData(completionHandeler: CompletionHandler){
let bookRef = self.dataBase.reference().child("books")
let GenreSelector = self.genreArray[indexPath.row]
bookRef.queryOrderedByChild("Genre")
.queryEqualToValue(GenreSelector)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {
print("Loading group \((child.key!))")
self.ResultArray.append(child as! NSObject)
}
print(self.ResultArray)
self.performSegueWithIdentifier("letsGo", sender: self)
self.tableView.reloadData()
})
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController: ResultTableViewController = segue.destinationViewController as! ResultTableViewController
DestViewController.SecondResultArray = self.ResultArray
}
You can inject the data to the destination viewController in prepareForSegue Method of the first UIViewController and reload your UITableView in viewDidAppear. If you are getting your data asynchronously, have a completionHandler and reload it in the completionHandler. Here is an example.
func fetchDataWithCompletion(response: (NSDictionary?, error:NSError?)-> Void) -> Void {
//make the API call here
}
How about this:
Assume you have an array (myArray) populated from Firebase and stored in the first tableViewController. There's a second tableViewController and a segue connecting them.
We want to be able to tap on an item in the first tableviewController, have the app retrieve detailed data for the item from Firebase (a 'data' node) and display the detailed data in the second tableViewController.
Firebase structure
some_node
child_node_0
data: some detailed data about child_node_0
child_node_1
data: some detailed data about child_node_1
Within the second tableViewContoller:
var passedObject: AnyObject? {
didSet {
self.configView() // Update the view.
}
}
Tapping an item in the first tableView calls the following function
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showListInSecondTable" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let tappedItem = myArray[indexPath.row] as! String
let keyOfTappedItem = tappedItem.firebaseKey //child_node_0 for example
doFirebase(keyOfTappedItem)
}
}
}
and the prepareForSegue then calls the following which loads the data from firebase and when the snapshot returns within the block, it populates the passedObject property in the second tableView
func doFirebase(firebaseKey: String) {
ref = myRootRef.childByAppendingPath("\(firebaseKey)/data")
//if we want the detailed data for child_node_0 this would resolve
// to rootRef/child_node_0/data
ref.observeSingleEventOfType(.Value, { snapshot in
let detailObjectToPass = snapshot.Value["data"] as! NSArray or string etc
let controller = (segue.destinationViewController as! UINavigationController).myViewController as! SecondViewController
controller.passedObject = detailObjectToPass
}
and of course in secondController, setting the passedArray calls didSet and sets up the view, and tells the tableView to reload itself, displaying the passed array.
func configView() {
//set up the view and buttons
self.reloadData()
}
I did this super quick so ignore the typos's. The pattern is correct and satisfies the question. (and eliminates the need for an observer to boot!)
P.S. this is way over coded but I wanted to demonstrate the flow and leveraging the asynchronous call to firebase to load the second tableView when the data was valid within the block.
Try updating your closure to include this:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
Edit:
On second read, you are already using a completion handler, but I think you didn't see it. Let me correct your code above a bit:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let bookRef = self.dataBase.reference().child("books")
let GenreSelector = self.genreArray[indexPath.row]
bookRef.queryOrderedByChild("Genre")
.queryEqualToValue(GenreSelector)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
// This here is your completion handler code!
// I assume it is called asynchronously once your DB is done
for child in snapshot.children {
print("Loading group \((child.key!))")
self.ResultArray.append(child as! NSObject)
}
print(self.ResultArray)
self.performSegueWithIdentifier("letsGo", sender: self)
// self.tableView.reloadData() // is this really needed
})
}
}
You defined a closure, but simply didn't call it. I don't see a reason for that anyways, assuming the block gets called once the database gives you your results. Am I missing something?
That's a good start already, but I think you didn't entirely get how to use a completion handler in this regard, but of course I may be wrong.
I built on top of user3861282's answer and created a small demo project at my github.
In short: You can do all inter-table-communication in the prepareForSegue: method of your first table view controller. Configure the second table view controller there (via its vars). Not any closures/completion handlers there yet.
Then in the second view controller's viewWillAppear: method, start the loading (including an animation if you want). I suggest something like NSURLSession that already defines a completion handler. In that you work with your data from remote, stop any loading animations and you're good.
If the completion handler must be defined in the first table view controller, you can even set it as a var in the second table view controller. That way you "hand over" the closure, i.e. "piece of code".
Alternatively, you can start animations and remote request in the first table view controller and then performSegueWithIdentifier once that is done. In your question you wrote that you want to load in the second table view controller, however, if I understood you correctly.
Your code above properly defines a closure that expects a completion handler (which is also a closure and so kind of doubles what you want), but you never actually call it somewhere. Nor do you call the completion handler in the closure. See my demo for how it can work.
The project I wrote illustrates just one way to do it (minus animations, not enough time). It also shows how you can define your own function expecting a completion handler, but as I said, the standard remote connections in the framework provide one anyways.
Based on additional code that was added to the post, the issue is a controller variable going out of scope.
So here's the issue
class MyClass {
func setUpVars {
let x = 1
}
func doStuff {
print(x)
}
}
Create a class and attempt to print the value of x
let aClass = MyClass()
aClass.setUpVars
aClass.doStuff
This will print nothing (conceptually) as once setUpVars ended, the 'x' variable went out of scope.
whereas
class MyClass {
var x: Int
func setUpVars {
x = 1
}
func doStuff {
print(x)
}
}
will print the value of x, 1.
So the real solution is that your viewControllers need to 'stay alive' during the duration of your class (or app).
Here's the pattern. In the MasterViewController
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
then in your MasterViewController viewDidLoad (or wherever), create the detailViewController
override func viewDidLoad() {
super.viewDidLoad()
let controllers = split.viewControllers //this is from a splitViewController
self.detailViewController =
controllers[controllers.count-1].topViewController as? DetailViewController
}
and from there you have it... use prepareForSegue to 'send' the data to the detailViewController
Just wanted to have this posted for future reference.
You can reload the TableView with [tableView reloadData];.

swift how to get self.tableView.reloadTable() when UITableViewController is not class type

I have a normal view controller with a table view inside, so the class is just a normal UIViewController, therefor I am unable to call self.tableView with this.
I have an asynchronous call to create an array of Aircraft objects that is taken from an online database, but that is only completed after the table cells are initially loaded. I am trying to update the table cells after this asynchronous call is completed, but am unsure how to do so.
I do the async call in viewDidLaod(). Here is my current code that only displays the loading tags since the update has not taken place for the aircraft array.
class AircraftViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let log = Logger( id: String(AircraftViewController.self) )
let dvc = DownloadViewController()
var aircraftArr = [Aircraft]()
override func viewDidLoad() {
super.viewDidLoad()
dvc.getMobileSystemOverviewHTML {
dispatch_async(dispatch_get_main_queue()){
self.aircraftArr = self.dvc.aircraft
self.log.debug("\n\n\n\n\n \(self.aircraftArr) \n\n\n\n\n")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table View Delegate Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("AircraftCell", forIndexPath: indexPath) as! AircraftCell
if indexPath.row < aircraftArr.count{
let singleAircraft = aircraftArr[indexPath.row] as Aircraft
cell.aircraft = singleAircraft
} else {
cell.aircraft = Aircraft(tailID: "Loading", aircraftSN: "Loading", Box_SN: "Loading")
}
return cell
}
You need to create the outlet of tableview like this
#IBOutlet var tableView: UITableView!
After that reload this tableview in your dispatch_async
dvc.getMobileSystemOverviewHTML {
dispatch_async(dispatch_get_main_queue()){
self.aircraftArr = self.dvc.aircraft
self.tableView.reloadData()
self.log.debug("\n\n\n\n\n \(self.aircraftArr) \n\n\n\n\n")
}
}
Hope this will help.

Unable to display data in the second view controller using segue [Swift]

I need to pass data from one view controller to another view controller. I used segue (detail) and define a model class named as "Photo".
TableViewController looks like the following:
var photos = [Photo]() //strongly typed swift array
override func viewDidLoad() {
super.viewDidLoad()
var newPhoto = Photo(name:"cat ", fileName:"cat", notes:"cat_file")
photos.append(newPhoto)
var newPhoto2 = Photo(name:"r2 ", fileName:"r2", notes:"r2")
photos.append(newPhoto2)
}
And the other view controller (DetailViewController) looks like the following:
import UIKit
class PhotoDiplayViewController: UIViewController {
var currentPhoto: Photo?
#IBOutlet weak var currentImage: UIImageView!
#IBOutlet weak var currentLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
var image = UIImage(named: currentPhoto!.fileName)
self.currentImage.image = image
self.currentLabel.text = currentPhoto?.name
println(currentPhoto!.name + currentPhoto!.fileName + currentPhoto!.notes)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I am running the program, the table view is loading fine and if i click on any cell it is going to the detail view controller. but noting is there in the detail view controller. And I used println() to check and the output is coming in the debugger like the following:
cat cat cat_file
To pass data, I used the following segue code block:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
var secondScene = segue.destinationViewController as! PhotoDiplayViewController
if let indexPath = self.tableView.indexPathForSelectedRow(){
let selectedPhoto = photos[indexPath.row]
secondScene.currentPhoto = selectedPhoto
}
}
But still no luck! Tried to figure out where I am missing? Can anybody tell me where I am lagging?
UPDATE: complete detail view controller class code
UPDATE: Full detail of My table view code
import UIKit
class PhotoTableViewController: UITableViewController {
var photos = [Photo]() //strongly typed swift array
override func viewDidLoad() {
super.viewDidLoad()
var newPhoto = Photo(name:"cat ", fileName:"cat", notes:"cat_file")
photos.append(newPhoto)
var newPhoto2 = Photo(name:"r2 ", fileName:"face.jpg", notes:"r2")
photos.append(newPhoto2)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return photos.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("photoCell", forIndexPath: indexPath) as! UITableViewCell
var currentPhoto = photos[indexPath.row]
cell.textLabel?.text = currentPhoto.name
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
var secondScene = segue.destinationViewController as! PhotoDiplayViewController
if let indexPath = self.tableView.indexPathForSelectedRow(){
let selectedPhoto = photos[indexPath.row]
secondScene.currentPhoto = selectedPhoto
}
}
}
Don't use a segue. Use this, its easier.
Follow these steps...
1: Create a Separate file called Manager.swift and place this code in it...
//manager.swift
import Foundation
struct Manager {
static var dataToPass = String()
}
2: Clean your project by pressing Shift+Command+K.
3: In the first view controller set the dataToPass to the data you want to pass...
Manager.dataToPass = self.dataToPass
4: In the second view controller retrieve the data and set the content to the dataToPass...
self.dataToReceive = Manager.dataToPass
5: Your Finished!!
The code which I presented is working fully after deleting all the image view and label from the storyboard and remapping. But I am wondering what was the problem. However, I want to share one screen shot:
In the screen shot, you will see 3 components: 2 labels and 1 image view. One 1 label's text is dark black colored but the for the other 2 it's not alike them. All of them are properly configured.
Still I don't know why this happens? I am not sure .... is it possible to add some hidden components on the top of storyboard??? or is it a bug of Xcode????
However, if you have similar experience please share. My aim is not only solve the problem but also to understand the cause of the problem.
:)

Change second table content based on first table row selected in swift

I have a view with two tables (propertyTypeList & propertyDetailList) and a text view (propertyDetailView)
What I'm trying to do is have propertyDetailList populate with an array based upon the selection made in propertyTypeList, then have propertyDetailView populate based upon the selection in propertyDetailList.
I can view the currently selected row using my bottom function and indexPath.row, but I can't get that call to work in the function above.
here is my code:
class NewProjectView: UIViewController {
#IBOutlet weak var propertyTypeList: UITableView!
#IBOutlet weak var propertyDetailList: UITableView!
#IBOutlet weak var propertyDetailView: UITextView!
var testarray = ["test1", "test2"]
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
if tableView == propertyTypeList {
return projectSources.count;
}
else {
return testarray.count
}
}
func tableView(tableView: UITableView!,
cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier:"Cell")
if tableView == propertyTypeList {
cell.textLabel?.text = projectSources[indexPath.row]
if indexPath.row == 0 {
println("Row 0")
}
else
{
println("Not Row 0")
}
return cell
}
else
{
cell.textLabel?.text = testarray[indexPath.row]
return cell
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
println(indexPath.row)
}
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.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
How do I access indexPath.row from the second tableView Func?
regarding your comment on #Duncan C's answer #BlueRad just reload your second tableView data in if statement
propertyDetailList.reloadData()
That will do the trick.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == propertyTypeList {
self.propertyDetailList.reloadData()
}
}
It looks like there is a typo in your question. You said:
I have a view with two tables (propertyTypeList & propertyDetailList)
and a text view (propertyDetailView)
What I'm trying to do is have propertyDetailList populate with an
array based upon the selection made in propertyDetailList, then have
propertyDetailView populate based upon the selection in
propertyDetailList.
Did you mean to say "What I'm trying to do is have propertyDetailList populate with an array based upon the selection made in propertyTypeList..." That would make more sense.
So you essentially have a master-detail setup, where propertyTypeList is a master table view and propertyDetailList is the detail table view.
I'm assuming that both table views have their data source and delegate pointed to the same view controller.
What you need to do is write your tableView:didSelectRowAtIndexPath:method to check the tableView parameter. That method will be called when the user selects a row in either tableView, but the tableView parameter will let you figure out which table view the selected cell belongs to.
So your code might look something like this:
func tableView(
tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if (tableView == propertyTypeList)
{
//Switch the selected item in the detail list
}
else
{
//do whatever is appropriate for selecting a detail cell.
}
}

Swift: How to load external array to my UITableView

I am learning Swift and I have pattern that I used to do in Objective C, but don't understand how to do it here.
I have UIViewController with TableView. I works fine when I put my array inside it. But according to MVC I want to move my array with data to another class. And I have no idea how to do it. Everything I tried doesn't work.
Thank you!
My code, how to move tableDS outside:
import UIKit
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
//temp table data
let tableDS = ["fdf", "dfd"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableDS.count
}
let textCellIdentifier = "TableViewCell"
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MyCell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as MyCell
let row = indexPath.row
cell.dayLabel.text = tableDS[row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
println(tableDS[row])
}
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel.text = tableDS[indexPath.row]
return cell
}
This should work.
If you want to use the MVC pattern, create a new singleton class, create the array there, then create a method returning the array.
First you need to initialize your table view with an empty array. When you load your MyViewController from another view controller in the code example below you can pass your data, and change your let tableDS = [“fdf”, “dfd”] to var tableDS = [“fdf”, "dfd"]. let is used for a constant variables.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YourMyViewControllerSequeId" {
let myViewController = segue.destinationViewController as MyViewController
var myArrayToPass = ["learn swift", "or get a life"];
myViewController.tableDS = myArrayToPass
myViewController.tableView.reloadData()
}
}
In the MVC design pattern for a table view the table view is the view object. The controller is the view controller.
The model is whatever you use to store your data.
The controller object serves as an intermediary between the model and the view.
For a simple table view the model object can be a as simple as an array. The array is the model. Thus there is no reason to store the data in a separate object.
If you really want to make your model a completely different object, create a new class. Call it MyTableViewModel. Make your MyTableViewModel class contain an array of your data. Also make MyTableViewModel conform to the UITableViewDatasource protocol. To do that, you'll have to implement several methods - in particular, cellForRowAtIndexPath.
Now in your view controller, create a MyTableViewModel object as a strong property of your view controller, install the array in it, and make it the data source of the table view.
Done.
Again, though, it's quite common to just treat a simple array as your model, and let the view controller serve up cells by implementing cellForRowAtIndexPath in the view controller.

Resources