Same view controller UI, different functionality - ios

I have a view controller whose UI is the identical between 2 classes, but the functionality is different. One of the classes uses the view controller to add a contact, the other uses it to edit a contact.
Is there a way to "reuse" the layout/view of the view controller while having different classes (add/edit class)?
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqual:#"AddContact"]) {
UINavigationController *navigationController = segue.destinationViewController;
AddContact *addContact = (AddContact *)navigationController.viewControllers.firstObject;
addContact.delegate = self;
}
else if ([segue.identifier isEqual:#"EditContact"]) {
EditContact *editContact = (EditContact *)segue.destinationViewController;
editContact.currentContact = [self.contacts objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
}
}
The segue.destinationViewController is of type ViewContact which both AddContact and EditContact both inherit from. All it does it hold onto the outlets for the textfields that both of its children use.
Unfortunately, the snippet above doesn't work because you can't really typecast parents to their children.

What I normally do is create a single view controller, with a xib included, and add a property like so:
header file
typedef NS_ENUM(NSUInteger, CRUD) { //Create, Read, Update, Delete
CTCreate,
CTRead
};
#property ( assign, readonly ) CRUD option;
And in the initialization of this view controller you'd have something like:
header
- (id)initWithOption:(CRUD)optionValue;
implentation
- (id)initWithOption:(CRUD)optionValue {
...
option = optionValue;
return self;
}
And in the implementation of this class you'd have if statements where the differences are, like when the user hits saves, should this class insert a new record, add, or update a recorded, edit
Hope this helps :) feel free to ask for more clarification :)p

Related

pass data from UITableCell to UITabView

I am currently working on a project that requires a list of customers to be displayed in a UITableView, the associated cell then segues to a TabView to display a detailed customer record in a tabbed ui.
I have setup the story board with the required TableView and populated fine. The TabViews all setup and I have added a custom class to the main TabView controller which can take the ID (required to interrogate service and return further data) and Customer Name. I have also added a UIViewController for the first tab in which I need to get the ID value.
I can't seem to get hold of the ID or Company Name that is passed. I have tried importing the .h file of the UITabView. I know the UITabView .h file is being populated with the values as in the .m file I am using the Customer Name to update the title of the Navigation Bar. However, whenever I breakpoint on line that gets the ID in the .m file for the individual tab, it always returns nil.
I am using the following code to try and get this value:
companyTabController *headerData = [companyTabController alloc];
_companyName_lbl.text = headerData.companyName;
_companyID_lbl.text = headerData.ID;
I have tried several variations of the above and all to no avail.
You can also use NSUserDefaults to save the data, I think that is the simplest way to save the data throughout the app.
From the code you posted, the headerData is a new instance. So the companyName and the ID will be nil unless you assign some value to them.
Since, you mentioned that you are able update the navigation bar title, try using the same object for fetching the values in this controller as well. (Maybe you can use a singleton object)
If your segueing you have to use the prepareForSegue:sender: method as such:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
companyTabController *companyTC = [segue destinationViewController];
companyTC.companyName_lbl.text = headerData.companyName;
etc
}
if your not segueing you will have to instantiate it as such :
- (void) didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
companyTabController *companyTC = [self.storyboard instantiateViewControllerWithIdentifier:#"CopmanyTabController"];
companyTC.companyName_lbl.text = selectedCell.textLabel.text or = headerData.companyName;
[self.navigationController pushViewController:companyTC animated:YES];
}

In iOS how to get object fromViewController name?

I know that this is double question. I know that I can use property in toViewController to get name of UIViewController to get NSString which tells me where I am coming from.
Anyway I want to ask if there a simple way to get name of UIViewController when unwinding from segue.
I have a UIViewController with segues to 3 forms. I programatically return to that view controller. I need to run a specific code only when I am returning from one of view controllers. My goal is using string from name of fromViewController start that specific code.
Using UIViewController by NSString from its class name isn't safe enough because the name can be changed.
You can use isKindOfClass instead:
UIViewController *destinationViewController = segue.destinationViewController;
if ([destinationViewController isKindOfClass:[MyViewControllerClass1 class]]) {
// put code related to transition to MyViewControllerClass1
}
else if ([destinationViewController isKindOfClass:[MyViewControllerClass2 class]]) {
// put code related to transition to MyViewControllerClass2
}
You can use:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
UIViewController *destinationViewController = segue.destinationViewController;
NSString * identifier = destinationViewController.identifier;
NSString * title = destinationViewController.title;
}
Create a Custom delegate method in the primary VC, create 3 strings with unique name so that u can identify.
EG.
NSString* stringFrmFORM1, *stringFrmFORM2, *stringFrmFORM3;
-(void)setString:(NSString*)myString{
//set the string from the VC1,2,3 to each string based on Primary VC's strings
}
Call the delegate method from each registration VC, and set those Strings.
You will have your registration strings to each of the Unique strings that you have set, from each of the Registration VC's.
To answer your base question, you can get the name of a class in string form with:
NSString *strClassName = NSStringFromClass([fromViewController class]);
but as #AlexPeda pointed out in ze answer, -isKindOfClass: would be better.
if ([fromViewController isKindOfClass:[SpecificViewController class]]) {
//run your 'specific' code
}

How to share a model with multiple Controller?

I've got a first UISplitViewController in wich there is (and it's default) two other view Controller as children and for one of them there is another view controller as child (again it's default).
My problem is that the Model, which is basically a class, used by the business logic is created in the AppDelegate and i'd like to use it in every controller.
I tried to use the viewDidLoad method to pass the model through all the controller but this method is called in the last child and then go through the hierarchical tree to the SplitViewController.
Two constraints i'd like to fullfill are:
I don't want to use a singleton
I don't want all my controllers to know the AppDelegate
Is there a way to to this?
If you don't want to use singleton for any reason you have you can just pass model through those ViewControllers. Just create custom initWithModel: method for each of ViewControllers you have when you create them and hierarchically pass data to them. Maybe create a base class for all of them to keep it common.
For storyboard you can assign model in prepareForSeque method, just after creating ViewControllers.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"myViewController"])
{
[segue.destinationViewController setModel:self.model];
}
}
That could be an answer :
in the .h :
+(MyModel *)shared;
in the .m :
static MyModel *myModel;
#implementation MyModel
+(MyModel *) shared{
if (nil != myModel) {
return myModel;
}
static dispatch_once_t pred;
dispatch_once(&pred, ^{
myModel = [[MyModel alloc] init];
});
return myModel;
}
In this way, you can access to your model anywhere of your app.
Hope that will help!
EDIT: add of dispatch_once

How to segue an object to a UITabBarController

I'm trying to segue an object from a table cell to a UITabBarController. My code to send the segue to a view controller is this:
#import "DetailViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateContacts"]) {
NSManagedObject *selectedDevice = [self.contactarray objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
DetailViewController *destViewController = segue.destinationViewController;
destViewController.contactdb = selectedDevice;
refreshControl = nil;
}
}
Also in DetailsViewController.h I´m adding the contactdb property
this one is working ok, now i don't know how to segue it to a UITabBarController so i can access it from my 2 view controllers that i have in the UITabBarController.
I'm not clear, is DetailViewController your TabBarController you are referring to?
Generally speaking, when you want to pass data between view controllers where the flow is a little more complex than normal, you can use a notification/listener pattern. For example, in the passing controller you would do something like this:
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_REFRESH
object:contactDb];
In your receiving controller, you would register to listen to the notification like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(gotData:)
name:NOTIFICATION_REFRESH
object:nil];
And in that same controller, here is the code to capture that notification and object:
- (void)gotData:(NSNotification *) notification {
NSObject *myData = [notification object];
}
You will need to define the notification types in a header like this:
extern NSString* const NOTIFICATION_REFRESH;
And it's implementation in the .m file like this:
NSString* const NOTIFICATION_REFRESH = #"notificationRefresh";
For that kind of purpose I usually keep a singleton object that is visible in any place of project (makin #import in .pch file). Then I declare all necessary propeties which I want to share between different objects (e.g. controllers) and use them by assigning values and accesing. For instance, in prepareForSegue I would assign contactdb property declared in that common singleton object and then get the value from other controller.

Is it safe to import a view controller to another controller for segueing?

I am currently going through the iTunes U Stanford iOS dev. course and I am trying to utilize segues.
In my prepareForSegue method I am trying to update the data on the transitioning VC and this is my code:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
"Code to be implemented"
}
}
}
But my Screen2ViewController isn't recognized. Is it safe and proper coding technique to import a view controller to another view controller for segueing purposes or is there another method I should implement?
-------------------------------------------------------------------------------------
I have a new problem now
When I set the values of a UILabel and UITextView with the aforementioned prepareForSegue method and change to Screen2ViewController the labels and text views have not be updated with the values that I have added.
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.myLabel.text = #"Screen 2 is now being viewed";
S2VC.uneditableText.text = #"Why aren't you showing up when I push you";
But these values don't get updated.
Yes it is safe to import view controllers. There are a few caveats however,
Do not import 2 headers into each other, this will cause non-obvious error.
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
#import "Screen1ViewController.h"
Import in the .m file instead
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
//No imports
Screen2ViewController.m
#import "Screen1ViewController.h"
As a general rule I try to put all the imports in the .m file: both for encapsulation and the above reason. You can also foreword declare a class if you need to use both classes in both header files.
About your new problem: you can only update instances from another view controller if they're made public (in other words, they're declared in its header file). So, with the provided code, you'd need to make myLabel and uneditableText public. However, during prepareForSegue: execution they were not yet allocated. As all you need from those objects is editing their text, it would be better to define two NSString's in the second view controller and then, inside that VC's implementation, you assign them to the objects. Example:
First View Controller
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.labelText = #"Screen 2 is now being viewed";
S2VC.textViewText = #"Why aren't you showing up when I push you";
}
}
}
Second View Controller's Header
...
#property (nonatomic, strong) NSString *labelText;
#property (nonatomic, strong) NSString *textViewText;
...
Second View Controller's Implementation File
...
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = self.labelText;
self.uneditableText.text = self.textViewText;
}
...
Needless to say you must have previously used the Interface Builder to add myLabel and uneditableText as #property's of your Second View Controller.

Resources