I have an NSManagedObject that is being used in my main view.
in this view I have two containers, each with their own static TableViews going on.
In my NSManagedObject I have an array I'd like to loop over, and display info on the screen like so:
Customer1 Name
Customer1 Type
Customer1 Address
Customer2 Name
Customer2 Type
Customer2 Address
I have tried to go the route of using a TableView, I have added a container, embedded the tableview in it, set a custom cell and tried to populate the custom cell with some test data. When I run it though the TableView just shows the four empty rows. (I'm probably missing something to do with the amount of rows which is why my test data isn't showing):
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tblPartyDetails.dequeueReusableCellWithIdentifier(
"JobViewPartyCell", forIndexPath: indexPath)
as! JobViewPartyCell
cell.lblPartyName.text = "test name"
cell.lblPartyAddress.text = "test adddress"
cell.lblPartyType.text = "test partyType"
return cell
}
I also have to figure out how to pass my NSManagedObject into this TableView class and it seems like a lot of effort for what is just a repeated block of information...or...is this the only way to do it?
So, am I going about this in the right way? If so, how do I fix it and add my NSManagedObjects details to the TableView. If I'm not going about this correctly, what are the alternatives? I had a look at some other custom 'card' type stuff, like facebook and google cards, but those techniques use custom TableViewCells as well.
edit. PrepareForSegue function:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == jobSegueIdentifier {
if let destination = segue.destinationViewController as? JobViewController {
if let jobIndex = tblJobs.indexPathForSelectedRow() {
let workItem:Work = fetchedResultsController.objectAtIndexPath(jobIndex) as! Work
destination.workItem = workItem
}
}
}
}
First of all you returned 0 in your numberOfRowsInSection and what you should do is putting the number of rows you want to display, if your are testing your tableView put any number.
And if your data is in your mainView you should pass your data to the contained tableView so you can display it and in your number of rows you should return the number of elements in your data array.
First give an identifier to your embed segue in the storyboard and in your main view implement the prepareForSegue function as follows:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "embedSegueIdentifier" {
let distinationVC = segue.destinationViewController as? EmbeddedTableViewController //replace EmbeddedTableViewController with your tableViewControllerClass
distinationVC?.dataArray = yourDataArray //yourDataArray is in your main view and you should define data array in your embedded table view controller
}
}
and in your tableViewController add the following:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
I hope this helped.
Related
I'm Trying to learn how to do a detail view for my project .
I have a simple tableView with a simple Array data to fill it.
The Table View :
TableView Example
I designed a detail View as well, with static tableViewCells
Detail View example :
Example
I'v Connected both with a segue :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("Profile", sender: indexPath);
}
I also connected all the labels and images with Outlets i want to change between each cell but i don't how to advance from here.
right now every cell shows the same thing but i want to change the data between rows . So i would like to change the data through the segue and create a master detail application like in my tableview. Can anybody help me ?
Am using Swift 2.3 and Xcode 8.1
If I understand your question correctly, you just want to pass dataSource element to the next viewController. So you can just pick it using indexPath.row and use sender parameter to set it in prepareForSegue method.
The code below assumes your dataSource is self.users array.
Swift 3
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let user = self.users[indexPath.row]
self.performSegueWithIdentifier("Profile", sender: user)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueId = segue.identifier? else { return }
if segueId == "Profile" {
guard let profileVC = segue.destination as? ProfileViewController else { return }
profileVC.user = sender as? User
}
}
Swift 2
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let user = self.users[indexPath.row]
self.performSegueWithIdentifier("Profile", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let segueId = segue.identifier else { return }
if segueId == "Profile" {
guard let profileVC = segue.destinationViewController as? ProfileViewController else { return }
profileVC.user = sender as? User
}
}
Edit
im trying to change data like al the labels you saw between rows like
for example shalvata will have a different data from light house and
so , change the labels and images and so on
It is still unclear for me what data you want to change exactly. Also I don't understand the language on your screenshots, but since you name the relationship as master-detail, I suppose the second screen is meant to show more info about the entity selected on the first screen.
If so, you should start from designing you model so that it contains all those fields you need on the second screen. Judging by the icons it would be something like
struct Person {
var name: String?
var image: UIImage?
var age: Int?
var address: String?
var phone: String?
var schedule: String?
var music: String?
var smoking: Bool?
var car: String?
var info: String?
var hobby: String?
}
Note: Remove ? for those fields which aren't optionals, i.e. always must be set for every entity (perhaps name field)
Usage
I don't known how and when you create your Person array, but basically there are two approaches:
Use a list of entities with all fields filled on MasterVC and just pass the selected person to the DetailVC in didSelectRowAtIndexPath
Use a list of entities with some basic data (name, address, image) required for MasterVC and fill the rest of the fields only when required (didSelectRowAtIndexPath method)
In any case you'll get selected person in DetailVC and now everything you need is to use that data in cellForRow method, just as you did on MasterVC. Perhaps it would be a better option to use static TableViewController for Details screen.
Sounds like what you're trying to do does not involve segues at all. You can change data of cells using the cellForRow method in your tableViewController.
https://developer.apple.com/reference/uikit/uitableview/1614983-cellforrow
For example
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "foo"
return cell
}
If that sounds confusing to you then you should take a step back and do some tutorials then post specific questions on SO.
There is a tableview with folder names and after clicking on each row, it segues to another TableView to show sections and rows for each folder. But I got problem how to show values corresponding to every folder. The error message is: fatal error: unexpectedly found nil while unwrapping an Optional value. Please see my codes below. I guess the problem is prepareForSegue codes didn't work well.
Note: Somewhere I need to delete half ")" as it doesn't work for "()" and I can't edit. Please help to edit.
In first tableview:
var folderNames = [String]()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == segueToDetailsTable {
let detailsVC = segue.destinationViewController as! DetailsViewController
detailsVC.detailsTableView.indexPathForCell(UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "DetailsCell")) == savingTableView.indexPathForSelectedRow
}
}
In second TableView to show details:
var sectionTitles = ["WebSite", "Date Saved", "Document Used","Add Notes"]
var detailsInSection = [[String](), [NSDate](),[AnyObject](),[String]()]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return detailsInSection.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTitles[section]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return detailsInSection[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DetailsCell")
cell?.textLabel!.text = detailsInSection[indexPath.section][indexPath.row].description
return cell!
}
You're doing some weird stuff in prepareForSegue. detailsVC hasn't loaded yet thus the tableview may still be nil. But most importantly, you cant connect the tables like that. When you select a row, figure out what data should be shown in the next. After you've aggregated the data set it as a property on the second vc inside the prepareForSegue method. When the next vc loads the property that it is using should already be set when the table loads. You also have two different data sources that tableview reads from. This is a bit dangerous because you cant guarantee there be a corresponding value in the other source.
I am new to swift as well as creating iOS apps and I thought I would make a simple app that calculates the averages of the numbers inputted into the TextField. The averageViewController also has a container view as well that contains TableView. Once the person has hit the "Next" button I would like the TableView to display the numbers that have been inputted. (each cell label has a single number).
This is my segue method in my averageViewController as well as the function I am using when the user presses the button:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "sendResult" {
let inputArray = segue.destinationViewController as! averageTableViewController
inputArray.arrayFromSegue = average.getArray()
}
}
#IBAction func nextButton(sender: UIButton) {
average.arrayInput(((inputTextField.text!) as NSString).doubleValue)
calcAverage()
inputTextField.text=nil
}
This is the code I made for my averageTableViewController:
class averageTableViewController: UITableViewController {
var arrayFromSegue = NSMutableArray()
var arrayUsed = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
arrayUsed = arrayFromSegue
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var Cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
Cell.textLabel?.text = String(arrayUsed[indexPath.row])
return Cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayUsed.count
}
}
I have done quite a bit of research and I believe one of my mistakes is that the action segue that I am doing (show) does not produce the correct results.
Problem isn't in segue you do. If you want change number of rows depend on number of input you should update your array data and reload your table. In your case you can change like this:
Create variable hold your tableViewController, in your case can put name is: inputArray
inputArray = segue.destinationViewController as! averageTableViewController
When you tap nextbutton you update array average and assign it to tableViewConroller `inputArray and reload it:
inputArray.arrayUsed = average.getArray()
inputArray.tableView.reloadData()
If you have any problem don't hesitate ask me. I will help you.
You can check my demo: Demo
Your project leak segue to tableviewcontroller: Please fix project like step below:
drage segue from average to averagetable
make it is embed:
Select it and name it sendResult
I know, I know this has been asked a lot of times. I also found this question but the solution it suggested did not work for me.
I am just trying to build an app to demonstrate how to use those things in UIKit (in case I want to use them later on. I can just copy the code).
I have created a View Controller with a table view in it. I wrote a class called PrototypeTableController to act as the view controller class for the view controller I created in the storyboard.
When the user taps on one of the cells, I want another view controller to show, called Prototype Table Content. And different text will be shown if you tap on different cells.
In the storyboard, it's like this:
The text of the label in Prototype Table Content will be different when the user taps on a different cell. This means I need to send data from one view controller to another.
The post mentioned above suggested that I should give the segue an identifier, so I did:
Here is my code:
View controller class for the table view:
class PrototypeTableController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let data = ["Cell1", "Cell2", "Cell3", "Cell4", "Cell5"]
let contents = ["Hello", "Nice", "OMG", "Jesus", "Peace"]
var content: String?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = data[indexPath.row]
return cell
}
func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "This is a prototype table view created by Sweeper"
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "my table"
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
content = contents[indexPath.row]
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("showContent", sender: tableView)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showContent" {
let destination = segue.destinationViewController as! PrototypeTableContentViewController
destination.contentString = content
}
}
}
View controller class for Prototype Table Content view:
class PrototypeTableContentViewController: UIViewController {
#IBOutlet var tableContent: UILabel!
var contentString: String?
override func viewDidLoad() {
super.viewDidLoad()
tableContent.text = contentString
}
}
I think I did all the things suggested in the post mentioned above. I added an identifier, I called performSegueWithIdentifier
, I also deselected the cell after the tapping.
However, it just doesn't go to the other view controller! It stays on the same controller! Like this:
When the user taps on one of the cells, I want another view controller to show, called Prototype Table Content. And different text will be shown if you tap on different cells.
While you can programmatically call performSegueWithIdentifier, it's a lot of effort that the storyboard can automatically handle for you. Just use a show storyboard segue from your prototype cell to PrototypeTableContentViewController.
prepareForSegue knows which cell you selected because the cell is the sender. All you have to do is set the destination view controller's contentString.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let controller = (segue.destinationViewController as? PrototypeTableContentViewController where segue.identifier == "showContent", let cell = sender as? UITableViewCell, textLabel = cell.textLabel else {
return
}
controller.contentString = textLabel.text
}
This is very similar to how a template like Master-Detail segues from a cell to show details about a cell (although Apple uses indexPathForSelectedRow to pass the cell's details):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row] as! NSDate
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
In either case, the SDK performs the storyboard segue for you; a segue didn't need to be programmatically added or performed.
Make sure your tableview delegate is set. If you are using storyboard, make sure delegate outlet in your storyboard is connected properly. If you are creating tableview by code, then you should do tableView.delegate=self; to set the delegate.
Your code is fine.
And one more thing:
You might need to change this line:
performSegueWithIdentifier("showContent", sender: tableView)
you need to make the sender as the row but not the tableview,so that the prepare for segue will get the sender as row instead of whole tableview.
As you are calling the prepareForSegue overtime you select a row, it makes sense to make the row as sender in performSegueWithIdentifier.
So it would be:
let row=indexPAth.row
performSegueWithIdentifier("showContent", sender: row)
I am currently making an app using swift that has information about cars.
I am using an UITableView for makes, models, years.
What I want to know is if can I have an UITableView linked to another UITableView depending on user input, for example:
tableview 1 (makes)
Audi
Honda
tableview 2 (Models)
Audi -> A1, A2, A3........
Honda -> Civic, Jazz...
tableview 3 (years)
Audi -> A3 -> 2005,2006,2007.....
Honda -> Civic -> 2005,2006,2007.....
Code for tableview 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.Makes = [Make(name: "Audi"),Make(name: "Nissan"),Make(name: "Fiat"),Make(name: "Ford"),Make(name: "Honda"),Make(name: "Mercedes-Benz"),Make(name: "Lexus"),Make(name: "BMW"),Make(name: "Vauxhall"),Make(name: "VW")]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.Makes.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
var make = Makes[indexPath.row]
cell.textLabel?.text = make.name
return cell
}
1.- Organise your data in a good manner, maybe a graph, a tree, or simply lists relating all of your data.
2.- For simplicity makes functions that will give you corresponding data to each tableview.
Lets say:
func getModels(make: Makes) -> [Model]
func getYears(model: Model) -> [Years]
or simply
func getModels(make: String) -> [String]
func getYears(model: String) -> [String]
also, some helper functions that will allow you to implement any data structure behind, just like, for example:
func getMaker(int:Int) -> Maker? or func getMaker(int: Int) -> String?
3.- You must keep in memory which of your possible makers and models have been selected, for now, keep it like:
var selectedMaker: String?
var selectedModel: String?
4.- I assume you will have all your UITableViews at the same UIViewController or UITableViewController, so you will need to decide corresponding data to show to every one.
For this you will need to differentiate each one, how is up to show, with tags, instance equality, etc. I suggest for later readability and facility of use to end up having a function that will return a number? maybe, corresponding to the tableview. For this explanation sake, lets call it func whichTableIsThis(tableView: UITableView) -> Int?
5.- Your delegates should work different for everyone of those tableviews. Here we will be using our brand new function that must return 1, 2 or 3 ..nil if this tableview is not one of those. :)
extension YourViewControlerWithTableViews: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//I'm assuming you will have only one cell, lets call it `AttributesTableViewCell`
var cell = tableView.dequeueReusableCellWithIdentifier("yourCellName", forIndexPath: indexPath) as! AttributesTableViewCell
cell.attributeValue.text = ""
if let tableNumber = whichTableIsThis(tableView) {
//here you will be checking for every of your tree cases, for this example I will check just for Models
//OK, so tableNumber returned 2
if tableNumber == 2 && selectedMaker != nil{
let value = getModels(selectedMaker!)[indexPath.row]
cell.attributeValue.text = value
}
//...
}
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let tableNumber = whichTableIsThis(tableView) {
//here you will be checking for every of your tree cases, for this example I will check just for Models
//OK, so tableNumber returned 2
if tableNumber == 2 && selectedMaker != nil{
return getModels(selectedMaker!).count
}
//...
}
return 0
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let tableNumber = whichTableIsThis(tableView) {
//here you will be checking for every of your tree cases, for this example I will check just for Maker
//OK, so tableNumber returned 1
if tableNumber == 1 {
selectedMaker = getMaker(indexPath.row)
//Here you must refresh data for your next tables in hierarchy, to allow them to refresh with new data
selectedModel = nil
selectedYear = nil
tableview2.reloadData()
tableview3.reloadData()
}
//...
}
}
}
And..that should be all. Hope it helps!
This approach of drilling down to see more details is very common, and Xcode even provides a template to illustrate this, called Master-Detail.
The way this works is when you select a row in the first (or Master) tableView, it performs a showDetail segue to the second (or detail) tableViewController.
In prepareForSegue, you would get the indexPath of the selected row, and pass the make to the detail (destination) view controller. That view controller would then show all the models for that make of car.
You would use the same process in the detail tableView to pass a specific model to the a third tableViewController to see all years for that make and model.
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
Let make = Makes[indexPath.row]
let controller = (segue.destinationViewController
as UINavigationController).topViewController
as! DetailViewController
controller.detailItem = make
controller.navigationItem.leftBarButtonItem =
splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Update:
The Master-Detail template provides other benefits, such as Adaptive UI. For example, on an iPad or iPhone 6 Plus, the user could choose to see both the master and detail views in a split view.