I am using iOS 5 SDK with arc. I want to pass NSObject from VC1 view controller to VC2 view controller, do modification and put back into VC1 controller. I don't want to point same nsobject from both VC1 and VC2 controllers. Whenever I am passing nsobject, it should create a copy of that nsobject and do the modification. (Do not modify actual nsobject).
I have tried below code but it crashes and giving error as
-[ImageObject mutableCopyWithZone:]: unrecognized selector sent to instance 0x1364ec20
Code:
I have NSObject as :
#interface ImageObject : NSObject
#property (copy,nonatomic) NSString *path;
#property (nonatomic, readwrite) int id;
#end
In VC1 view controller:
I am passing my nsobject to VC2 view controller as follows:
VC2ViewController *vc2 = [[VC2ViewController alloc] initWithNibName:#"VC2ViewController" bundle:nil];
vc2.imageObj = [imgObj mutableCopy];
[self.navigationController pushViewController:vc2 animated:YES];
In VC2 view controller:
VC2ViewController.h file
#import "ImageObject.h"
#interface VC2ViewController : UIViewController
#property (retain,nonatomic) ImageObject *imageObj;
#end
VC2ViewController.m file
// modifying nsobject as below
-(void)modifyObject
{
UIViewController *previousViewController = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
if ([previousViewController isKindOfClass:[VC1ViewController class]])
{
VC1ViewController *parent = (VC1ViewController *) previousViewController;
if(parent != nil)
{
_imageObj.id = 2;
[parent reloadData:_imageObj];
}
parent = nil;
}
[self.navigationController popViewControllerAnimated:YES];
}
Have any idea ? How to resolve this issue?
Your ImageObject class needs to conform to the NSCopying Protocol.
This answer here explains better and shows you how the code looks like.
I also think that you need to use [imgObj copy] instead of [imgObj mutableCopy] because according to apple docs:
The NSMutableCopying protocol declares a method for providing mutable copies of an object.
Only classes that define an “immutable vs. mutable” distinction should adopt this protocol.
Classes that don’t define such a distinction should adopt NSCopying instead.
Related
I have a UIViewController Class. In that class I implement a UIWebView. On that WebView I got data from CocoaHttpServer and loading a html page. In CocoaHTTPServer have a NSObject Class. In that Class I got one method for pop to previous Screen. How to Pop to view controller from NSObject class.
This is my NSObject class
if([[array lastObject] compare:#"back-button-clicked"] == NSOrderedSame) {
[self handleBackActionRequest];
}
-(void)handleBackActionRequest
{
PGSoftApViewController *softAP = [[PGSoftApViewController alloc] initWithNibName:nil bundle:nil];
[softAP webToBackNativeCall];
}
This is in my ViewController class :
-(void)webToBackNativeCall
{
[self.navigationController popViewControllerAnimated:YES];
}
This is not working. I am able to insert webToBackNativeCall method. But its not happening anything.
I have to go back. How can I. Please help me.
In your NSObject.h class add protocol like this
#protocol nsobjectDelegate <NSObject>
-(void)navigateToBackVC;
#end
#interface objectClass:NSObject
//your other code
#property (nonatomic,weak) id<nsobjectDelegate> objDelegate;
#end
In you NSObject .m class
-(void)webToBackNativeCall
{
// this method you already have
// other code if you want
[self.objDelegate navigateToBackVC];
}
In you view controller .h class
#interface objectClass:NSViewcontroller <nsobjectDelegate> // delegate of NSObject include it in VC when you want to call that methods
In you view controller .m file in viewDidLoad or willAppear or DidAppear method initializ object of NSObject class and set delegate
NSObjectClass *objeObject = [[NSObjectClass alloc] init];
objeObject.objDelegate = self;
implement this method
-(void)navigateToBackVC
{
[self.navigationController popViewControllerAnimated:YES];
}
This is general concept of protocol and delegate.
Hope this might help you.
In viewDidLoad of your ViewController
[[myNSOBjectClass alloc] startViewController:self];
In NSObject Class
#interface myNSOBjectClass{
//Global access to the view controller.
UIViewController *viewController;
}
#implementation myNSOBjectClass
//function that the caller calls to pass their UIViewController
- (void)startViewController:(UIViewController *)callerViewController{
viewController = [[UIViewController alloc]init];
//puts the caller's view controller to the global member.
viewController = callerViewController;
}
I have this problem that I want to hide the container view from the parent VC when I tap a table cell at the last VC on the right.
I tried hooking up a protocol delegate but it did not work.
Here is my failed attempt:
I added the Protocol on the GOCategoryMenuViewController.
#protocol GOCategoryMenuViewControllerDelegate <NSObject>
-(void)hideMenu;
#end
#interface GOCategoryMenuViewController : UIViewController
#property(weak, nonatomic) id <GOCategoryMenuViewControllerDelegate> delegate;
#end
Implement hideMenu in the .m file
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate hideMenu];
}
Import the header and delegate to the parentVC
#import "GOCategoryMenuViewController.h"
#interface ViewController : UIViewController<GOCategoryMenuViewControllerDelegate>
Implement the hideMenu in the .m of ParentVC
-(void)hideMenu
{
NSLog(#"Hide the menu");
}
I believe I need to also declare self on the delegate, but I don't know how and I tried searching for a solution on the web to no avail. It's why I'm asking directly now to you all. Please help and thanks!
Your protocols is true, only thing you have to do is when you init instance of GOCategoryMenuViewController you should give it a delegate sth like this:
GOCategoryMenuViewController *category = [[GOCategoryMenuViewController alloc] init];
//if you don't give delegate, never your delegate method will get called
category.delegate = self;
You have to delegate your instance of GOCategoryMenuViewController class.
If you want to solve it with delegates only, then you must pass the parent view controllers controls delegates to the child view controllers with declaring the properties that has the same type with the parent control in the child view controller. Then you can do any operation on the parent view's control in the child view controller.
ParentViewController.h class
#interface ParentViewController : UIViewController
{
NSMutableDictionary *dictOfPeople;
}
#property(nonatomic, retain) NSMutableDictionary *dictOfPeople;
ParentViewController.m class
#synthesize dictOfPeople;
- (void)anyMethod
{
//initialize child view controller
ChildViewController *childViewController = [[ChildViewController alloc] init];
// here is important
childViewController.owner = self;
}
ChildViewController.h class
#interface ChildViewController : UIViewController
{
id owner;
}
#property(nonatomic, assign) id owner;
ChildViewController.m class
#synthesize owner;
- (void)anyMethod
{
// You can access parent view controllers dictOfPeople dictionary like this
int totalNum = [((ParentViewController *)self.owner).dictOfPeople count];
//...
}
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...
In UINavigationController this is child controller
.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller
.m
-(void)childMethod:(NSArray *)params {
// some work
}
...
childVC *cVC = [[childVC alloc]init];
cVC.pVC = self;
But childMethod: is not getting called so I searched on internet and got this post
UINavigationControllers: How to pass value to higher (parent?) controller in stack?
I tried to create a weak reference but dont know how to use to make delegate pass data from child to parent?
Try this. Check the sample project attached
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
- (void)passData:(NSString *)strText;
#end
ParentViewController.m
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSString *)strText {
NSLog(#"Data Passed = %#",strText);
}
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[self.delegate passData:#"Hello"];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Sample Project
This is child controller.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller.h
#import <UIKit/UIKit.h>
#import "ChildController.h"
#interface perentController : UIViewController < childProtocol >
.m
- (void)childMethod:(NSArray *)params {
// some work
}
EDITED :
And Dont Forget to add childViewOBJ.childDelegate = self; at the time of create ChildViewController's object. such like,
childVC *cVC = [[childVC alloc]init];
cVC.childDelegate = self;
cVC.pVC = self;
[self presentModalViewController:cVC animated:YES];
For More information about How to create/use of Protocol.
First of all, you are not checking for the same selector as you declared in your protocol declaration so it won't respond to that. You declared the method childMethod: whereas you are checking if your childDelegate responds to myMethod: selector which does not so it won't go into the if condition.
Also the parent view controller is missing the implementation the method childMethod: in its .m. Implement that in your parent view controller or it will crash because of not finding the exact selector definition.
Since you are using a UINavigationController, the parent view controller won't be lost till the child view controller exist so the childDelegate property must not be strong unless you intend to hold onto your delegate in child view controller for some reason.
I am trying to equal the string of an UITextView from anotherViewController to the MainViewController . I mean users write something in UITextView from anotherViewController and when touch the done button the string in UITextView should equal to another string in MainViewController . How can I access UITextView ???
I did something like this but did not get any result !
#import "AnotherViewController"
#class AnotherViewController;
#interface MainViewContoller : UIViewController {
NSString *main_string;
AnotherViewController *textEntered;
}
-(void)viewDidLoad {
textEntered.TEXT = main_string
}
***TEXT is the name my UITextView on AnotherViewController .
EDITED :
#interface AnotherViewController : UIViewController {
UITextView *TEXT;
id delegate;
}
#property (nonatomic, retain) IBOutlet UITextView *TEXT;
#property (nonatomic ,retain) id delegate;
#end
#implementation AnotherViewController
#synthesize TEXT ,delegate;
- (IBAction)doneButton {
//!!! problem with compare ! the compiler got me some warning
[self dismissModalViewControllerAnimated:YES];
}
MAIN VIEW CONTROLLER
#import "AnotherViewController.h"
#class Another...;
#interface MainViewControlelr : UIViewController {
NSString *main_string;
TextViewController *textEntered;
}
- (void)viewDidLoad
{
textEntered.delegate = self;
}
- (void) compareText {
[textEntered.TEXT isEqual:main_string];
}
Set mainViewController object as the delegate of an object of anotherViewController type. And use that delegate from AnotherViewController to pass messages to MainViewController.
Inside MainViewController, when you create your AnotherViewController object do:
anotherVC.delegate = self;
And in AnotherViewController, when your button action is done and you want to pass the text back, say store in a string. Then pass it to MainViewController as:
[delegate compareText:textView.text];
compareText with a NSString argument would be a method in your MainViewController class.
EDIT:
In your AnotherViewController.h interface, declare an ivar:
id delegate;
Then use #property (in .h) and #synthesize (in .m) for getter and setter.
Then do the above.
I think you created an AnotherViewController pointer but did not set it.
before you can access textEntered.TEXT, you should set you textEntered pointer to your AnotherViewController object.
Here is how:
on iGhalamViewController class, create a property for setting textEntered object like this in your interface declaration (probably in iGhalamViewController.h)
#property (assign) AnotherViewController *textEntered;
So, at the root place where do you create this viewcontroller objects both. After you create AnotherViewController and iGhalamViewController, set your textEntered pointer like this.
AnotherViewController* a = [[AnotherViewController alloc] initWithBlah:blah];
iGhalamViewController* b = [[iGhalamViewController alloc] initWithBlah:blah];
b.textEntered = a;
Then it should work like you expected. But use it in other method than viewDidLoad. viewDidLoad probaby gets called before you can set textEntered. A button press event method should be fine.