How to programmatically set cell.textLabel.text from a different view? - iphone

I've got a view controller, call it VC1, that's a table view. When I tap a cell in the table view, I am presented with a new view controller, call it VC2, which is a short list of choices. After making a choice, I want to dismiss VC2 and set the cell.textLabel.text property of the VC1 cell I originally tapped to the value I selected in VC2.
Conceptually speaking, what is the proper way to do this? I've tried a handful of different approaches, but all of them seem wonky at best, and only one of them actually worked - although it was the most cumbersome of all, passing references to both view controllers and table view cells and all kinds of things. It just feels like I'm making a mountain out of what is probably a mole hill.
This is such a common paradigm that I find it hard to believe there's not a simple method for doing it.

There are a number of ways to handle this but one of the most flexible is via the Delegate Pattern. Define a delegate protocol in VC2's interface and then make VC1 conform to that protocol. When you create VC2 assign VC1 as it's delegate (similar to the way you did with your UITableView). One requirement of your protocol should be a method like didFinishWithStringSelection: (or whatever you call it) where you would update the table cell and reloadTable.

I do kind of that with with a table view presenting several attributes of an data object. To change a single attribute the user has to select the specific table cell showing the attribute he wants to change and that will push a new view controller (with a picker in my case) where the user can change the value from a selection.
I assign the data object to a property of the new controller before pushing. With this the value can be changed directly in the data object, and when i return (via navigation controller) to the first view controller there is a reloadData in viewWillAppear.
Did you try it this way?

Related

Swift - Reuse the old instance of view when switch between Views and Passing data over views

I am trying to create a simple app and here is how it works:
When the user clicks the button "Picking", it'll show a tableview. And when the user selects a row in the table view, it will show the first view and display the data on the label.
I have 2 problems:
Every time I click the button "Picking", a new instance of the table view controller gets created; but does not reuse the existing one. How can I make it reuse the old one?
I want to pass the selected data to the root view. I tried by creating a segue to show the root view when user selects a cell and then passing the data on prepareForSegue method. But, I faced the above problem of memory leak. How do I pass the data between screens without leaking memory?
It sounds like you may be making a circular view hierarchy. When you tap on the cell in the table view, if you're pushing a new instance of the first VC, that is the wrong way to do it IMO.
Instead, you should pass the data back to the first VC then dismiss the second VC. Then, when you tap on the "Picking" button again, it's a new instance of the table view but your previous instance has already been removed from memory.
This allows for the smallest amount of memory to be used at any given time. When the table view controller isn't on the screen, it doesn't need to be in memory.
IMO, the best way to pass data up a navigation stack is to use a protocol and delegate. It's similar to how Objective-C protocols and delegates worked if you're familiar with that. Here is Apple's documentation on both. The delegate section is about 1/3 the way down the page.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

Setting up Transitions/Segues from different elements of a Table View using Storyboards

I believe my question is relatively basic but I still haven't found something that directly answers it. I'm trying to learn Objective C and iOS Programming using Storyboards and am in the process of building my first app now. The way I have it set up right now is, I have a Navigation Controller that goes to a Table View controller (controller A). I'm passing an array to my table view and I have the disclosure indicators on the different elements in my array. I also have a second different view controller (controller B) with a table view in it. I want different elements of controller A to transition to different views/controllers etc. I'm familiar with the concept of segues and the prepareForsegue method however, I dont need prepareforsegue here since I'm not really passing anything to the next screen. I just want to be able to click different elements of my Table View in controller A to bring me different views (i.e like controller B and other views that I plan to build). Is there something I'm not seeing or do I need to fundamentally change my design?
Perhaps you're thinking of creating different prototyped cells?
Here's how I understand your question:
I have two (or more) different kinds of cells where I know the view to which they would transition beforehand (when your table queries the delegate).
I would like those cells to go to different controllers rather than the same controller.
If this is right, check it out:
Create a new UITableViewCell in the table by dragging from your kit
of UI objects.
Give each cell a reuse Identifier
Control+Click and drag to your target view controller
You'll get a popup asking what type of segue and what UI object triggers the segue
Then you'll see two segue indicators leaving your UITableViewController
Finally, make sure you dequeue the right type of cell:
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"CellReuseIDA"];
return cell;

UITableViewController -> EditView - data flow

I have a UITableViewController and in the didSelectRowAtIndexPath method I create an instance of a UIViewController and push it on to the stack.
The UIViewController is meant to edit the content of the selected cell but how do I get the changes made in the ViewController transfered back to the TableViewController?
Cheers
The standard way to pass data back up the hierarchy is with delegation or even more simply through querying a property on the edit view controller when you want to get the edited data back.
Now, when it comes to edit view controllers you can design them to support both direct editing of 'live' objects, or a copy which will let you do a Save/Cancel model.
So what you want is for your edit view controller to edit some sort of ModelObject instance with various properties. These properties will correspond to the textfields or date-pickers etc. in the view. So you might have a Person with NSDate *dateOfBirth and NSString *name.
When you create the view controller and push it onto the navigation controller, you give it one of these ModelObjects to edit. You can either pass in an object straight from your model which will be edited 'live' as the user enters values, or a copy which will let you discard changes and implement a Save or Cancel workflow. For the latter you can add the Save and Cancel buttons yourself before you push the edit view which lets you handle the actions yourself without needing delegation.
So your edit view controller will set the properties on this object when the user enters a new value in a textfield or changes the date picker. For a live object these changes will be applied immediately to your model. For a copy, when the user hits Save you query the edit view controller for the object you passed in and merge/copy that back into your model. If the user hits cancel, you just discard the object.
Override the "parent" or table view controller's -viewWillAppear: method and reload the table view data there, using [tableView reloadData];.
When you go back to the table view controller from the edit view controller, the table view controller's -viewWillAppear: method reloads the data, which in turn calls the table view delegate methods.
You have to make sure that the two controllers 'share' data somehow. The easiest option is to have the first controller pass in the array (or just the selected object) that is selected in the table. The second view would then directly modify that array (or single object) and then when you dismiss the second view will show the modified data.
You might have to reload the table.
Makes sense?

Reusing UIViewController for modal and non-modal situations

I have a UIViewController — let's call it "FormController" — which is simply a form that edits an object. I want to use it in 2 different situations:
Creating a new object — using using UINavigationController's presentModalViewController: method.
Editing an existing object — push the view controller onto the UINavigationController stack, not using a dialog method.
There is a slight difference in that in the modal situation I would like to have a toolbar with "Cancel" and "Done" buttons, whereas in the stack situation I would like to just have the navigation bar provided by the UINavigationController.
This would be similar to the Contacts application where the "New Contact" and the "Edit Contact" screens seem to use the same view controller, but the New Contact form is presented modally while the Edit screen is pushed onto the navigation stack.
My question is: What is the best way to handle both situations without having to write 2 separate, but mostly identical view controllers?
I thought about creating a "ModalFormController" which encapsulates the bare "FormController" through composition and adds a toolbar, but I read somewhere in the docs that Apple doesn't recommend nesting view controllers.
Why not use subclassing? Make ModalCreateFormController a subclass of EditFormController and handle the modal-specific stuff in the subclass.
What I do (sometimes) is set up an enum that specifies the type of the view controller.
For example, you might have two types: an Edit type, and an Add ("new") type.
The Add type is implemented via a modal view controller, while the Edit type is pushed onto an existing navigation stack.
In the view controller's -viewDidLoad: method, I simply do a switch/case tree that sets up the title and other appearance characteristics depending on the type enumeration specified above.
The nice thing about this is that it is easy to add a new type. The downside is that the conditional tree for handing this enumeration can get complicated quickly, depending on how different the types are.
But the switch/case tree makes it a lot easier to manage.
So, it depends on what you're trying to do with the two types. But it's definitely doable.
In addition to having an explicit property on the view controller (as Alex Reynolds suggests), two other approaches that occur to me are:
If you have some kind of model object that you're editing, ask it for its current state. If it's ever been saved, then you're in edit mode. Otherwise, you're in create mode.
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your list controller.
Ug, I hate extra ivars…
I use this instead:
if([[self.navigationController viewControllers] objectAtIndex:0] == self){
//Modal
}else{
//Pushed
}
It is a bit of a hack, but we are using the logic that if the offending view controller is the first in the stack, you can't go back. Actually we are ignoring the fact of whether it is modally displayed at all.
I had to do this a bunch of times in my app and after trying a couple different ways of doing it, including modal subclasses & a re-usable modal helper classes that used forwardInvocation. I found the best pattern was to make a containingModalViewController method each view controllers that (usually) creates and returns a UINavigationController for the caller to use with presentModalViewController.
In most cases this method builds and returns a UINavigationController with self as the root view controller (with repeated calls to the method checking self.navigationController and returning that instead if it's not nil). Other cases I made a dummy root controller first and pushed self on second in order to get a back button. Then a trick can be used to catch the back button press: http://smallduck.wordpress.com/2010/10/05/intercepting-uinavigationcontroller/
In some cases the view doesn't need a navigation bar and so this method just adjusts some flags and returns self. I even found in some cases that did need a navigation bar it was simpler to make that method invoke self.view, then tweak the view hierarchy to add a UINavigationBar and again return self. But in any case, the setup often isolated to that one method, and the caller handles it the same in each case.
Apple explains how the contacts application works under the hood:
To allow a custom view controller class to be used to both display and edit content, override the setEditing:animated: method.
You get some functionality for free, e.g. Edit/Done button.

Can I subclass a view built with Interface Builder?

I have a view which I built in Interface builder with a tableview and associated outlets etc, to display a list of items. Clicking on an item brings up the detail of that item.
I now want to build a very similar view with a list of the same kinds of items and some additional controls and different behavior on cell selection. In this case, if the user clicks on the item, they will effectively be using that item as a template to create a new item. Or, they can click on a button to create a new item. Aside from this, there is a lot of logic I want to reuse from the original view - for example the items are location dependent, and there is a background thread that updates the location information.
It seems like the natural thing to do would be to subclass the original viewcontroller, and build a second view layout using IB. However, before I embark on this I'm wondering if this is possible/recommended practice? Will IB recognise the IBOutlets in the superclass and let me wire them up?
IB will recognise the IBOutlets defined in the superclass.
You do it all the time: The outlet for the view in UIViewController is defined in a superclass you subclass for each of your View Controllers.
pgb is correct. IB will recognise outlets in the superclass
As for whether subclassing the controller is a good or bad idea, I'm not sure. Apple suggests subclassing NSArrayController in order to change sorting/filtering behaviour. A view controller is different to an array controller, but I dare say that subclassing the view controller is probably the correct thing to do.