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.
Related
I am looking for a solution to my situation. My app is as followed:
On VC1 there is a textfield and button. User types a name. Then click on a button. This button open VC2. User gives additional information in VC2 then press save. I used a segue to go back to VC1 and transfer those additional information as a string to VC1. But in VC1 viewdidload has called and due to that information in the textfield is deleted!!! How can I navigate between VC without recalling viewdidload?
I found some information about singleton objects. If I can define an object which is alive during time I go through VCs would be great. How can I have an object with multiple fields and alive all the time.
I am afraid that why you are using push segue to move back.
Here you need to assing a push segue from VC-A to VC-B and name its identifier like moveForward and button press call
[self performSegueWithIdentifier:#"moveForward" sender:self];
and if any information u want to pass to VC-B pass it in this method
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"moveForward"])
{
VC-B* vcObject=[segue destinationViewController];
//vcObject.info = your info//etc
}
}
In a same way when u have to return to VC-A from VC-B assign a rewind segue from VC-B to VC-A and name its identifier like moveBack and on button press
and do the above mention method in VC-B too.
viewDidLoad method is called only once per lifecycle of UIViewController, so most probably you are creating somehow new VC1. You need keep reference to first VC1 and go back to that view controller.
To navigate using UINavigationController use those methods:
[self.navigationController pushViewController:VC2];
[self.navigationController popViewControllerAnimated:YES];
(call them inside VC1 / VC2)
here is how you can define singleton class that is called AppShareData :
AppShareData.h
#interface AppSharedData : NSObject
+(AppSharedData*)sharedInstance ;
#property (nonatomic) BOOL sharedBoolVariable ;
#end
AppShareData.m:
#implementation AppSharedData
#synthesize sharedBoolVariable;
+(AppSharedData *) sharedInstance
{
static AppSharedData *_sharedInstance = nil;
static dispatch_once_t Token;
dispatch_once(&Token, ^{
_sharedInstance = [[AppSharedData alloc]init];
});
return _sharedInstance;
}
#end
and then if you want to edit or set the value of the variable in any class i would do the following :
-(void)editMethod
{
AppSharedData * dataObject = [AppSharedData sharedInstance] ;
dataObject = YES ;
}
and if i want to retrieve the value of the variable in any class i do the following :
-(void)retrieveMethod
{
AppSharedData * dataObject = [AppSharedData sharedInstance] ;
BOOL someVariableInMyClass = [dataObject sharedBoolVariable] ;
}
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
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
}
I have a function called sendDataToMotor. It is in my First View Controller classes. I have another view controller called SecondViewController. I need to call this function from the Second View Controller.m class. I tried declaring the property:
#property(nonatomic,assign)UIViewController* firstController;
in my SecondViewController.h class. Furthermore, I wrote the code bellow in the viewDidLoad part of my SecondViewController.m class (where I want the function to be called).
secondViewController = [[SecondViewController alloc] initWithNibName:#"secondViewController" bundle:nil];
secondViewController.firstController = self;
[self.firstController performSelector:#selector(sendDataToMotor)];
But, Im getting an error with the first word in that code (secondViewController) because of an undeclared identifier issue. Furthermore, I get an error with the second line (secondViewController.firstController = self) because secondViewController has an unknown name type.
In summary, I don't care if you use the above code to answer my question: that was just something I tried to implement that I found online. However, I'm looking for the simplest way to call a function from another View Controller.
Notification Center could be solution to you question.
Receiver UIViewController
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"myNotification"
object:nil];
}
- (void)receiveNotification:(NSNotification *)notification
{
if ([[notification name] isEqualToString:#"myNotification"]) {
//doSomething here.
}
}
Sender UIViewController
- (void)sendNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:self];
}
You want to use the delegate pattern, and you are almost there.
This line in secondVC:
#property(nonatomic,assign)UIViewController* firstController;
should be generalised so as not refer to a specific type
#property(nonatomic,weak)id <delegateProtocol> delegate;
And you should accompany that with a protocol declaration in the header of your secondVC (above the #interface declaration), something like
#protocol SecondVCDelegate
- (void) sendDataToMotor;
#end
In firstVC interface you can declare your adherence to the delegate protocol in the first line of the #interface declaration in the header file
#interface firstVC < SecondVCDelegate >
Or in the first line of a private interface declaration in the .m file
#interface firstVC() < SecondVCDelegate >
Then you won't need to use performSelector (which anyway should be preceded by a safety check) as the compiler will alert you of errors.
In firstVC after creating secondVC, set it's delegate property to self(i.e. firstVC)
secondVC.delegate = self;
then in secondVC you can just call the method directly on it's delegate:
[self.delegate sendDataToMotor];
I explain this in more (wordy) detail here...
https://stackoverflow.com/a/14910469/1375695
There are many problems in your code. I'm going to assume the second chunk of code you included is actually in -viewDidLoad in FirstViewController not the second.
The error you're getting is because you aren't putting the type before secondViewController. It should be SecondViewController *secondViewController = ...
This probably still won't work for you because when you perform your transition to the second view controller, you won't be using the same object.
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.