Access A UIImageView Outside Of Where It Was Declared - ios

I have declared a UIImageView (IBOutlet UIImageView *character;) in a file called GameViewController.h and I am unsure how I can access this from another file CharacterSelectViewController.h.
All I need to do with it after I get it is to set the image. How can I access this UIImageView from another file?

First you need to get the existing instance of GameViewController that is currently on display. How you do that from CharacterSelectViewController depends on how they are related to each other and you may want to set up a delegate relationship / navigate the view controller stack to find the correct instance.
Once you have the instance you just access the property:
gameViewController.character.image = ...;
In any case, a delegate type relationship is generally better than navigating the view controller hierarchy. This could be as simple direct relationship or a generalised relationship using an #protocol.
If GameViewController presented CharacterSelectViewController as a modal, then you could use
GameViewController *gameViewController = (GameViewController *)self.presentingViewController;
If GameViewController pushed CharacterSelectViewController into its navigation controller, then you could use:
NSArray *viewControllers = self.navigationController.viewControllers;
GameViewController *gameViewController = (GameViewController *) viewControllers[viewControllers.count - 2];

Related

iOS - how to target the "parent" viewcontroller with initWithViewController

I would like to use a method in a viewcontroller that is the "container" of an other viewcontroller but not directly the parent. I display a popover containing a custom xib file. For that I have three viewcontrollers:
1) popoverVC managing the settings of the popover (like alpha value, any arrow, size, and the method I want to use from "outside" dismisspopover)
2) customVC with a xib file to change the content of my popover (some text and some buttons).
3) mainVC from where the popover is launched and where I receive some actions when interacting with my buttons in customVC. mainVC is the delegate of customVC.
My problem is I don't know of to call dismisspopover method in popoverVC from an action in customVC. At the very beginning I initialize my customVC like this from mainVC:
mainVC.m
customVC *cvc = [[customVC alloc] init];
[cvc setDelegate:self];
PopoverController *popover = [[PopoverController alloc] initWithViewController:cvc];
[popover presentPopoverFromView:textField];
I tried to instantiate a new instance of popoverVC inside my action method in customVC to be able to call the method [popoverdismiss]. But this should not be working since I will be targeting a different instance from the one I started with... And I would like to be able to target popoverVC like [self.presentingVC] but I am not sure the method initWithVC sets a hierarchy like this.
Does anyone has an idea?
The way I usually go about having to circumvent the default view controller hierarchy is to create a property within your child view controller (I think customVC) called parent or something to that effect that's of type mainVC. Then either create a new initializer for your customVC that incorporates a field to assign to parent or just assign to it after you first initialize your customVC. Then whenever your customVC needs to ask the mainVC to perform a function, you can make a method call from parent.
Alternatively, you could just as easily make parent correspond to your popoverVC object, I'm not totally sure which object you need to access, but it should be as simple as creating your own property for it.

How to pass data to a programmatically created child view controller

In my app, I have created a child view controller that I have instantiated with a storyboard ID. I then programmatically add this child view to a UIScrollView which is why I can't simply create a container view in the storyboard. Heres what I need to still do: I need to pass data to the child VC. I know how to pass data while using a segue to a VC, but how would I perform this operation in this case?
The controller that you're adding the child to has a childViewControllers property that contains any children that controller has. If you only have one child, then you can reference it from the parent with,
SomeClass *child = self.childViewControllers[0];
child.someProperty = self.propertyIWantToPass;
Consider you want to send string data to ViewController2,ViewController3 from ViewController1.
Make property of the string variable in ViewController2 and ViewController3.
#property(nonatomic,strong) NSString *str;
And while pushing the ViewController2 and ViewController3:
ViewController2 *viewController = [ViewController2 alloc]init];
viewController2.str = #"Some text";
[self.navigationController pushViewController:viewController2 animated:YES];
And you have the data send from ViewController1 in ViewController2.
What about making a final destination for the data you want to pass in the child.
#interface ChildVC : UIViewController {
#property (nonatomic) id myPrivateData;
}
- (id)initWithData:(id)data;
#end
Then passing in the data with the -initWithData: when you instantiate.
ParentVC *vc = [[ParentVC alloc] init];
vc.dataIWantToPass = ...;
ChildVC *child = [[ChildVC alloc] initWithData:vc.dataIWantToPass];
It's been a while since I've done iOS,
After looking at Apple's documentation I'm pretty sure you can't add a ViewController to UIScrollView. You need to give it something that inherits UIView. You could subclass your UIView and give it properties or a method to pass data into before giving it UIScrollView.
UIScrollView inherits from UIView so it has the property UIScrollView.subviews which can be used to access the VIew you passed into it, if the reference is no longer available.
If you're only manipulating a UIViewController or one of it's subclasses then you can access the subview it manages through its property UIViewController.View

Send UITableView row From One tableviewController to another

I want to send a UITableViewCell at indexPath.row from one controller to another. I can remove the row using removeObjectAtIndex, but unable to send the removed row into another controller.
I'm trying to store the removed row in an NSMutableArray in one controller, but don't know how to populate it in another controller.
Below is the code -
ViewController *view= [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
view.anotherviewArray= [self.arrayFromAFNetworking objectAtIndex:indexPath.row];
If anyone can give me an idea, it would be helpful.
I believe it's bad practice to retain UI elements and pass them around your app. You should instead have some kind of a model containing your data, and pass this model from one view controller to the other. I'd recommend checking out tableview frameworks such as the free Sensible TableView framework, as they do an excellent job of providing such a model for you automatically.
I personally think that it's wrong approach to pass UI object as parameter to another controller.
As I would do it is create some object that encapsulates data model from this cell and pass this object to another view controller.
#interface DataObject : NSObject
#property id field1;
#end
UI part of cell can be easily copied in Interface Builder, so I don't see problem in that. Probably it would be great to have cell class that could fill necessary field from the object with data. This class you can use in both view controller that have to show the same cell
#interface CustomTableViewCell : UITableViewCell
- (void)customizeCellWithDataObject:(DataObject *)dataObject;
#end
I hope it makes sense to you
Assuming that you DO want to set the other data source with only this row, you need to pass it as an array.
view.anotherviewArray= [NSArray arrayWithObject:[self.arrayFromAFNetworking objectAtIndex:indexPath.row]];
But it's hard to tell from the little code that you have provided. I assume that since you are instantiating the viewController you are also transitioning to it below the provided code. If you are trying to set the array for a viewController already presented, you need to access that one, not create another, perhaps by having saved a reference to it an ivar within the current viewController or another accessible class.
I would also not name a ViewController view, it is confusing to anyone reading the code later on.
Editing for my comment below about traversing the hierarchy. Here is some code that I used in one iPad project to return the final presented viewController. This method is in the appDelegate. It is somewhat specific to my project, where there is only one navigationController. But you can adapt it to yours. You would test for a viewController that is of the class of your target view controller.
- (UIViewController *)topViewController {
UIViewController *rootViewController = self.window.rootViewController;
UIViewController *topViewController = rootViewController.presentedViewController;
while (topViewController && topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;
if ([topViewController isKindOfClass:[UINavigationController class]]) {
UIViewController *presentedViewController = [(UINavigationController *) topViewController topViewController];
if (presentedViewController) {
topViewController = presentedViewController;
}
}
}
return topViewController;
}
The other approach is to set a property to it when it is created and presented. We don't have enough code to get a good idea of your app as a whole. Where are you creating the ViewController instance that you are displaying? By that I mean where you are calling a segue to it, or pushing it onto a navigationController or call presentViewController. Wherever that is, you need to set a property or ivar to it. Let's say that you use a property in your appDelegate as a very generic case.
In your MyAppDelegate.h file you have
#property(nonatomic,strong) ViewController *viewController;
Wherever you first create it you set that property
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
I now think you are trying to add this to a mutableArray in the other ViewController. Then replacing your code from the tableViewCell above you would use
MyAppDelegate appDelegate = (MyAppDelegate)[[UIApplication sharedApplication] delegate];
[appDelegate.viewController.mutableDataArray addObject:self.arrayFromAFNetworking objectAtIndex:indexPath.row];
[appDelegate.viewController.tableView reloadData];
I will say that it is not great practice to use the appDelegate for the property. But it is a generic answer that would work. It's best to put it in a class which is common to the viewControllers that you are passing data between. Perhaps a single parent which holds these two viewControllers?

instantiateViewControllerWithIdentifier and pass data

I am using Storyboard in my app and I want to pass data from one view to another view.
Instead of using segues I am using instantiateViewControllerWithIdentifier. In this case I am instantiate from my first TableViewController to a NavigationController which has a second TableViewController attached because I need the navigation in the second TableViewController. Now I want to pass data from my first TableviewController, depending which row was clicked, to my second TableviewController. In this case newTopViewController would be my NavigationController but my problem is now how to pass data from firstTableViewController to the secondTableviewController.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%#Top", [menuArray objectAtIndex:indexPath.row]];
UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
}
If you instantiate a navigationController, you can use the viewControllers property to get the inner viewController of the navigation controller.
Something like this:
UINavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
MBFancyViewController *viewController = navigationController.viewControllers[0];
// setup "inner" view controller
viewController.foo = bar;
[self presentViewController:navigationController animated:YES completion:nil];
newTopViewController.anyVariableToShow= anyVariableToSend;
I do this pretty often on a few of my apps...
//Create new VC
CookViewController *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"CookVC"];
//Set recipe
[detailViewController setRecipe:recipe];
//Pop over VC (can be pushed with a nav controller)
[self presentPopupViewController:detailViewController animationType:MJPopupViewAnimationFade];
If you aren't using a navigation controller or segues, then I think you need to reconsider your app design.
Actually it's not just a data pass problem as this is a program control and data transfer question together.
Even you would have to rethink about your app's concept, as you'd like to use storyboard without the meaning of storyboard, it's up to you and I hope you have good reason to do what you do.
So when you decided not to use segue you lost the new and comfortable way of instantiating a new controller and transferring data with it and you have to do the transfer of control and the data in two distinct steps. When you instantiate another scene in storyboard (like you do with instantiateViewControllerWithIdentifier:) you just instantiated a new controller and transferred the control but not the data. Just think about it as you instantiated a new controller from a xib in an old way (so you have to use initWithCoder: or awakeFromNib in the second view controller as the storyboard will not call initWithName:bundle:), but did not do anything more.
So you will have a new controller (it named in the identity part of the second storyboard) which is hanging in the universe without any relationship or connection with anything else (as the storyboard picture illustrates it nicely) and you could do with it what you'd like.
So you'd like to do something and you need data from the previous storyboard (ViewController). What you need is making available those data to the second storyboard(ViewController), and as you know there are lot of solution for this which were available long time before even storyboard is existed.
So regarding your code, the "data transfer" is depending on your design, whether the two controllers are subclasses of each other or whatsoever...
If you don't like to deal with subclassing and like to decoupling them as much as possible, the best way just make a property of your data in the first controller and refer to them from the second (after importing the first's .h file) and just refer to it in it's viewDidLoad or in initWithCoder: or anywhere where you need them, as
secondViewControllerdata = firstViewControllerdata.thatDataProperty
Of course you can do the same in reverse and make a property of the second controller and refer to it in your first view controller.
You can define some parameter in UIViewController to receive data:
#property (assign) int param1;
#property (retain) NSMutableArray *param2;
and use below to pass the data:
[newTopViewController setParam1:XX];
[newTopViewController setParam2:XX];

instantiateViewControllerWithIdentifier in Storyboard

I've made a tabbar controller with two views (standard setup from the template). First view is accessing an array within an dataController object. When I tap to the second view on the tabbar, I want the dataController reference to be set on the second viewController's dataController property.
I use call
SecondViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc.dataController = self.dataController;
However, when I call my countArrayObjects on the secondViewController's dataController property, I always get 0 back - eventhough there should be objects in the array and I can't figure out why?
I've set the identifier on the secondViewController in MainStoryBoard and I have no crashes, the property just isn't set (I think).
For testing purposes I call the instantiateViewControllerWithIdentifier from a simple button, before tapping on to the secondView. And I call the countArrayObjects from viewDidLoad on secondViewController.
Any ideas?
Hmmm... I was having the same problem. At the moment, the only way I got around this is to test for the views superview being nil - which as its accessing the view selector of the class fires up the view did load event...
both of us are doing something wrong here though...
This is the test code I wrote...
SummaryViewController *sc = [self.childViewControllers objectAtIndex:0];
if(sc.view.superview == nil) sc.checkInTime.alpha = 0;
//-- by inspecting the value, it will call the viewDidLoad method of the controller.
It works, but it's wrong.

Resources