Objective-C type downcasting - objective-c

I am trying to override
- (void)makeWindowControllers;
Here is the code for it:
NSStoryboard* const storyboard = [NSStoryboard storyboardWithName:#"Main" bundle:nil];
NSWindowController* const windowController = [storyboard instantiateControllerWithIdentifier:#"Document Window Controller"];
And then I would like to add an image to ViewController that user has selected through open... in Swift I would simply do:
(windowController.contentViewController as? ViewController)?.imageView?.image = openedImage
How I could do this downcasting in Objective-C? I really got confused since I haven't done much type converting while I was learning C. Thanks.

[[((ViewController *)[windowController contentViewController]) imageView] setImage: openedImage];

Related

how to pass information from Objective-c's viewcontroller to a Swift storyboard

I started to work in a project which was already started in ObjectiveC and without Storyboard. I'm making the new sections in Swift and with Storyboard, but now I'm having a problem when I try to pass information (an UIImage) to my storyboard's controllers.
This is how I go to the ViewControllers:
-(void)expenseCamera:(VOCCustomCameraVC *)camera didFinishPickImage:(UIImage *)
image imageCreated:(NSString *)imageCreated
fileName:(NSString *)fileName {
[camera dismissViewControllerAnimated: YES completion: nil];
UIStoryboard *addExpenseStoryboard = [UIStoryboard
storyboardWithName:#"addExpense" bundle:nil];
CreateExpenseVC *createExpenseVC = [addExpenseStoryboard
instantiateViewControllerWithIdentifier:#"expenseNavController"];
UIImage *compressedImage = [UIImage compressImage: image];
createExpenseVC.expenseImage = compressedImage; // It crashes here
//createExpenseVC.delegate = self;
[self presentViewController: createExpenseVC animated: YES completion: nil];
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}
If I take out the createExpenseVC.expenseImage = compressedImage everything goes well, but when I try to pass the image I get this error:
SIGABRT *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[YourApp.CustomNavController setExpenseImage:]: unrecognized selector
sent to instance 0x12e09b200'
The destination viewController starts this way
#objcMembers class CreateExpenseVC: UIViewController {
/* ---- IBOutlets ---- */
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var backButton: UIBarButtonItem!
#objc var expenseImage: UIImage?
And in the containerView I show another viewController where I pretend to show the image.
I tried to change the code I use to navigate to this
CreateExpenseVC * createExpenseVC = [[CreateExpenseVC alloc] init];
But it also crashes because (I think) it's not instantiating the Storyboard and therefore not recognizing all the IBOutlets and so on...
So how can I pass information like this?
Many thanks in advance
I tried to replicate your problem with the way you have implemented and it worked fine for me.
But your error said, that [YourApp.CustomNavController setExpenseImage:] is not implemented. That means your view controller is embedded inside a navigation controller.
And you are trying to get navigation controller by this code
[addExpenseStoryboard
instantiateViewControllerWithIdentifier:#"expenseNavController"]
so your createExpenseVC is actually of type Navigation controller and not CreateExpenseViewController.
And that is why it doesn't get the expenseImage.
That is where your problem lies
Here is what you can do..
UIStoryboard *addExpenseStoryboard = [UIStoryboard storyboardWithName:#"CreateExpense" bundle:nil];
UINavigationController *navController = [addExpenseStoryboard instantiateViewControllerWithIdentifier:#"ExpenseNavC"];
CreateExpenseViewController *createExpenseVC = navController.viewControllers[0];
UIImage *image = [UIImage imageNamed:#"383975"];
createExpenseVC.expenseImage = image;
[self presentViewController: navController animated: YES completion: nil];
When dealing with this issue, I would check multiple things.
Is storyboard ID assigned?
Is the correct UIViewController class assigned?
Is the UIViewController has view outlet?
Check these screenshot for a visual guide:
View controller identify inspector screenshot
View controller view outlet screenshot

Pass data between view controllers

I’m still fairly new to Objective C and xCode so please forgive me.
I have two view controllers setup in my storyboard and im using the following code to move to another:
UIStoryboard * storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController * viewController = [storyBoard instantiateViewControllerWithIdentifier:#"stageOne"];
[self presentViewController:viewController animated:YES completion:nil];
Now this works great though in the class controlling the second view controller I have a property setup that Im attempting to send data to. I was attempting the following:
UIStoryboard * storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController * viewController = [storyBoard instantiateViewControllerWithIdentifier:#"stageOne"];
viewController.forename = #"Hello World";
[self presentViewController:viewController animated:YES completion:nil];
Though this does not work and all I get is an error "Propert 'forename' not found on object of type 'UIViewController'".
Any help is appreciated and if you are able to leave me with some example code that would be fantastic.
The problem with your code is your accessing the uicontrols which are not yet created in the memory. You have to change the order of the execution of your statements like this...
UIStoryboard * storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
holidayQuestion * viewController = [storyBoard instantiateViewControllerWithIdentifier:#"stageOne"];
[self presentViewController:viewController animated:YES completion:nil];
viewController.forename = #"Hello World";
Basically, for pass data between 2 controllers, you can use 2 design patterns: Delegation or Observer.
In your case, you use delegate. Do you have public property with name forename in your header file?

Call storyboard scene programmatically (without needing segue)?

I have a modal storyboard scene that I want to be accessible to all my other scenes. Creating a modal segue to it from every scene on my storyboard creates a big mess of strings going everywhere. Is there a way that I leave off the segues and call the scene programmatically instead?
Basically I want to do something like this:
MyNewViewController *myNewVC = [[MyNewViewController alloc] init];
[self presentModalViewController:myNewVC animated:YES];
except instead of creating and pushing a view controller class, I want to do a modal transition to an "isolated" (not connected with a segue) storyboard scene.
Yes you can. Do something like this to get access to the VC, then just Modal Push it:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
MyNewViewController *myVC = (MyNewViewController *)[storyboard instantiateViewControllerWithIdentifier:#"myViewCont"];
Note: the method presentModalViewController:animated is deprecated in iOS 6.
The new code should read:
NSString * storyboardName = #"MainStoryboard_iPhone";
NSString * viewControllerID = #"ViewID";
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
MyViewController * controller = (MyViewController *)[storyboard instantiateViewControllerWithIdentifier:viewControllerID];
[self presentViewController:controller animated:YES completion:nil];
In the storyboard give your view controller an identifier (under the Attributes Inspector) then use the following code to bring that view forward.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"STORYBOARDNAME" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"VIEWCONTROLLERIDENTIFIER"];
[self presentModalViewController:vc animated:YES];
I have a case where I want to present a view controller from the main part of the app, one with settings & help & so on. To do this, I want it to be within a nav controller, sort of a little plug in module we can call from a UIBarButtonItem.
Now, this can be to/in the current storyboard, or to another, it doesn't matter.
I want to do it this way, because I loathe the potential of segue line spaghetti all over my storyboard.
Here's how to do it.
- (IBAction)displaySettings:(id)sender
{
LOG_SELECTOR() // google that for extra goodness
// FYI, this can be done using a different storyboard like so.
/*
NSString * storyboardName = #"MainStoryboard_iPhone"; // possibly use device idiom?
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
*/
// To push a new set of scenes with a new Navigation Controller, it is done like this:
UINavigationController *settingsNC = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings Nav Controller"];
OBSettingsUIViewController *settingsVC = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings root"];
[settingsNC pushViewController:settingsVC animated:NO];
[settingsNC setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
// Present the view controller;
[self presentViewController:settingsNC animated:YES completion:NULL];
}
In the presented view controllers (or in a subclass of the Navigation Controller), you can have a UIBarButtonItem to then dismiss the whole presented hierarchy of view controllers like so:
- (IBAction)dismissThisVC:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Hope this helps a bunch of people out. Cheers.
Just call viewcontroller using navigation controller
Write this code in viewcontroller and set viewcontroller in storyboard as set in the image.
ProfileVC *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ProfileVC"];
[self.navigationController pushViewController:vc animated:YES];
Call to navigate to other class
UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UINavigationController *navController = (UINavigationController *)window.rootViewController;
DumpFeed *dump = [storyboard instantiateViewControllerWithIdentifier:#"DumpFeed"];
dump.isPushed=YES;
dump.strUserId = appDelegate.strFriendid;
[navController pushViewController:dump animated:YES];
Heres a Swift version of this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myVC = storyboard.instantiateViewControllerWithIdentifier("myStoryId")
self.presentViewController(myVC, animated: true, completion: nil)
You should also change your storyboard id like this:
I think that with iOS7 it has become very easy implementing via the storyboard
I'm currently learning about the new features in iOS7 and found this simple solution, but it might have been relevant even in prior versions, I'm not sure.
First u need to connect the presenting VC with the target VC (thats the only connection needed), then within the storyboard's attributes inspector choose the style to be modal, in the identity inspector give your VC a storyboardID and make sure you checked the 'use storyboardID' checkbox,
If its not there yet add this method to your presentingVC:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
YourTargetVC * targetVC =
(YourTargetVC *)segue.destinationViewController;
if(nil != targetVC) {
//Do preparations here
}
}
Now, when you wish to show your targetVC from your presentingVC you can use:
[self performSegueWithIdentifier:(NSString *) sender:(id)];
where the identifier is your viewController's storyboardID, and the sender is the view who triggered the action, this method will invoke the storyboards scene, so the [prepareForSegue: sender:] method will be called allowing u making last modifications before the targetViewController will appear.

Switch between view controllers

I want programatically switch from one view controller to another. I use the following code :
SenderPlayerViewController *myViewController = [[SenderPlayerViewController alloc] init];
[self.navigationController pushViewController:myViewController animated:YES];
but I get a black screen, while I have already put some controls in SenderPlayerViewController.
Are u trying to instantiate with an nib file? If so, you must use initWithNibName:bundle:
SenderPlayerViewController *myViewController = [[SenderPlayerViewController alloc] initWithNibName:#"SenderPlayerViewController" bundle:nil];
Also with a story board it is not enough to call alloc init on a controller class.
It should be something like
UIViewController *viewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:#"SenderPlayerViewController"];
If you are using a view controller from a storyboard you should use
SenderPlayerViewController *myViewController=[storyboard instantiateViewControllerWithIdentifier:#"TheNameOfYourController"]
instead of alloc init.

adding subview of another class

iv created a class with xib so i can access it throughout my app. The class basically holds a nib with has three uiviews and a few buttons buttons+labels. Now im calling class A (the one with 3 view etc) from classB but every time i addsubview to self.view nothing happens. any help appreciated.
ive done the following in class B.h
#import "PlayResultViewController.h"
PlayResultViewController *playResultViewController;
in classB.m
//viewdidload
playResultViewController = [[PlayResultViewController alloc]init];
//some random method
[placeholderView addSubview:playResultViewController.loseView];
You are missing the initWithNibName to start with, here are some examples
With a Navigation controller u can use
BViewController *bController = [[BViewController alloc] initWithNibName:#"BViewController" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];
without UInavigation controller you can test with
BViewController *bController = [[BViewController alloc] initWithNibName:#"BViewController" bundle:nil];
self.view = bController;
// or alternatively self.view = bController.view;
[bController release];
You need to tell it which nib to load....
playResultViewController = [[PlayResultViewController alloc] initWithNibName:#"Mynib" bundle:nil];