I'm programming an application for road safety, and there will be a lots of code, so what I thought is I will divide each function into a class.
Now I'm trying to set the background of the main ViewController From another class, however it doesn't work. This is the code that I tried:
-(void)StartBackgroundAnimation
{
ViewController *MainRoot = [[ViewController alloc] init];
MainRoot.BackgroundViewer.image = [UIImage imageNamed:#"Lunch_Background.png"];
NSLog(#"ok");
}
What I did is that I created a function called StartBackgroundAnimation, and I imported the main view-controller class, and I created an object from it. After that, I imported this class to the main view-controller, and I called this function but the image doesn't work.
Note: NSLogworks perfectly and the function is called however the image doesn't change.
If you really want to store functions in different classes just return a UIImage and assign it in the view controller.
So inside your custom class.
- (UIImage*)backgroundImage{
return [UIImage imageNamed:#"Lunch_Background.png"];
}
Inside of your main view controller:
self.view.backgroundView.image = [instanceofyourclass backgroundImage];
If you'd like to access data from a class outside of a class, it is usually best in Objective-C to use a property.
// MyClass.h
#interface MyClass : Superclass
#property (strong, nonatomic) UIImage * backgroundImage;
#end
This way, you can read/write the property by using self.backgroundImage from within the class and anotherClass.backgroundImage from outside of it.
Edit: spelling...
Related
I have two scenes that I made in SpriteBuilder, one is Shop and the other is UpgradesNew. Shop is a CCNode layer and UpgradesNew is a CCNode layer. I have two CCScrollViews in the MainScene that load Shop and UpgradesNew respectively.
When one button in Shop is tapped, the label in UpgradesNew should change colors. I have been trying to implement this using delegates but it's not working.
Here's what I did...
In shop.h I set the protocol:
#protocol changeColorProtocol <NSObject>
#required
-(void)changeColor;
#end
Then I set the id
#property (nonatomic, retain) id <changeColorProtocol> delegate;
Here is the button that when clicked, should use changeColor. This is in shop.m
-(void) buyDiggerShibe {
[self.delegate changeColor];
[self didLoadFromCCB];
}
Now in UpgradesNew.h I made it adopt the protocol like this
#interface UpgradesNew : CCNode <changeColorProtocol>
And in UpgradesNew.m
I set delegate to self in ViewDidLoad.
Shop *shop = [[Shop alloc]init];
shop.delegate = self;
.
-(void)changeColor {
if (hasDigger == YES) {
shovelRequires.color = [CCColor greenColor];
NSLog(#"HEY HEY HEY");
}
}
I probably have parts of the delegate placed in the wrong area because I was trying to switch them around when it wasn't working, I'm not sure where they are supposed to go. I've watched multiple delegate tutorials and it just seems overly complicated, at least with what I am trying to do.
Any ideas?
EDIT:
Tried this.
I created a property in UpgradesNew
#property (strong, nonatomic) Shop *shop;
Then I synthesized it in the implementation and allocated it like this in didLoadFromCCB, instead of creating a new object:
self.shop = [[Shop alloc]init];
shop.delegate = self;
EDIT: This is how I am creating objects.
Drag a label into a layer. Identify it then define it in header as CCLabelTTF *label; That's it, thats all I do to create any object on the layer.
To create a layer like Shop or UpgradesNew, I hit New -> File -> Layer. That creates a new CCNode. Then I set the class of the CCNode, as shown in the picture the CCNode that is highlighted has a class of MainScene. If I want to establish a #property to that CCNode I just type the name in the box right below custom class and set it as doc root var, and then put it in the header as CCNode *MainScene. I don't do anything other than that.
I don't know anything about SpriteBuilder, so it's a bit hard to address your question. You might want to add SpriteBuilder to the title of your post so people who use that framework are likely to read it.
You need to explain how the 2 "CCNode layer"s are created, and how you link them together. In order for one object to have another object as a delegate, the delegate property has to be set somewhere. Where is that setup being done? Have you set a breakpoint at the line
[self.delegate changeColor];
To make sure that self.delegate is not nil?
Have you set a breakpoint in your changeColor method, or added a log statement, to see if it's being called? My guess is that self.delegate is nil, so the messages is being dropped on the floor (it's legal to send messages to nil in Objective-C. It just doesn't do anything.)
I have animation layers stored within SpriteBuilder. I am calling it on a touch began method
heroCharacter.m
#implementation heroCharacter{
CCNode *_heroNode;
staminaNode *_staminaReference;
}
- (void)touchBegan:(UITouch *)touches withEvent:(UIEvent *)event
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
}
This works fine.
I am then trying to call the animation in a custom method in another file but it then doesn't work. I have no idea why? I have tested to make sure the method is called and it is. but the actual animation isn't called. It is the same code as used in the touchBegan
-(void)sleepingHero {
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
No Idea how to debug this.
My custom method is being called like this in another file. Called bedroomScene.:
#implementation .....{
heroCharacter *heroHolder;
}
then in didload:
heroHolder = [[heroCharacter alloc] init];
then in another method:
[heroHolder sleepingHero];
By executing:
heroHolder = [[heroCharacter alloc] init];
in your other file, you are instantiating a new node that has no relationship whatsoever with the original _heroNode node in your first class.
What you should do is passing the actual _heroNode from your first class to your second class, so that you can reference it in the latter. There are multiple ways of doing it:
you could define a property in your second class and set it from the first (supposing the first class instantiates the second);
you could add an argument to your second class' initialization method and pass _heroNode in it;
or, you could expose _heroNode in your first class through a property (or custom accessor method) and then pass a reference to the first class into your second class.
Hope this helps.
EDIT:
you could try something like this (where you currently create your bedroomScene):
bedroomScene = [BedRoomScene scene];
bedroomScene.heroHolder = _heroNode;
For this you will need to make your heroHolder ivar into a property:
#property (nonatomic, weak) CCNode* heroHolder;
(in your BedRoomScene interface or class extension).
When is sleepingHero being called? If it is called before didLoadFromCCB is called it means that the code connections are not set up yet and you cannot reference anything created in SpriteBuilder.
I have a UIViewController class and a UITableViewController class. Within the UIViewController class I have an NSMutableArray.
I now have the issue of how to load data into my table view, a separate class, I must access the NSMutableArray I used to populate the previous UIViewController class.
I tried using a delegate to access the array in the UIViewControllerClass however the array had "0 objects" and was NULL
I would appreciate some guidance in the right direction here.
You could have one view controller hold a reference to the other view controller and query the public NSMutableArray on it for data. Aaron suggested this and it might be your best solution.
Or.. you have multiple view controllers trying to access the same set of data. Potentially you have other classes which will want to access this data also. You might want to consider pulling the data out of the view controller and storying it in a neutral location. You could store it in the AppDelegate and then reference the app delegates from any place you need it.
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
NSMutableArray *myData = appDelegate.data;
You could also consider pulling all the logic of your data and the data itself into a separate class and use a Singleton It would allow you to access/manipulate the data fairly easy from anywhere.
The last 2 methods would insulate data from user interface controller objects and prevent the need from potentially unrelated objects needing to hold references to one another. Used properly it will reduce code complexity and mage future changes easier to manage.
Create an NSMutableArray property on your UITableViewController class like so:
#interface CustomTableViewController : UITableViewController
#property (strong, nonatomic) NSMutableArray *dataFromOtherClass;
#end
And then when you transition, perhaps like this, you can set the dataFromOtherClass property:
CustomTableViewController *controller = [[CustomTableViewController alloc] initWithNibName:#"CustomTableViewController" bundle:nil];
controller.dataFromOtherClass = myNSMutableArrayData; // <-- Set data like this
[self.navigationController controller animated:YES];
// Or ...
[self presentViewController:controller animated:YES];
// Etc...
I am developing and iOS app for iPad. I have an UIView subclass and I'd like to call a method of the ViewController from that subclass. I've tried to code a delegate but it doesn't work. Is that a good solution or I have to do it another way?
Try block, here is the sample:
MyView.h
#interface MyView: UIView
...
#property (nonatomic, copy) void(^myEventCallBack)(id someData);
...
#end
MyView.m
(How to call block sample)
...
- (IBAction)buttonTapped:(UIButton *)sender {
if (self.myEventCallback) {
self.myEventCallback(self.someImportantData);
}
}
...
in your UIViewController:
self.myView = [[MyView alloc] initWithFrame:someFrame];
// THIS IS VERY IMPORTANT, USE __weak COPY OF YOUR UIViewController OBJECT (owner of object which contains block) INSIDE BLOCK TO PREVENT RETAIN CIRCLE, CAUSE BLOCK RETAINS ITS CONTENT
__weak MyViewController *self_ = self;
self.myView.myEventCallback = ^(id someData){
[self_ doSomeProcessingWithData:someData];
};
also block can be used with return value, sample:
#property (nonatomic, copy) BOOL(^shouldStartProcessing)(void);
self.myView.myEventCallback = ^BOOL{
return (self_.state == MyViewControllerStateReady);
};
In general the problem of communication between conceptually "distant" objects is tricky one, but it is the heart of Cocoa programming. Getting a reference from one instance to another is crucial! I discuss the problem in general here:
http://www.apeth.com/iOSBook/ch13.html#_instance_visibility
Here's one possibility. If this UIView instance is in the interface, then either its nextResponder is the view controller or it is the subview of a superview whose nextResponder is the view controller. Moreover, the view hierarchy parallels the responder chain. So simply walk up the responder chain until you come to the view controller.
UIResponder* r = someView; // the view instance, living in the interface
while (![r isKindOfClass:[UIViewController class]])
r = [r nextResponder];
Now r is the view controller. Now cast to what you know is the actual view controller's class, and you will be able to call methods on it:
MyViewController* vc = (MyViewController*)r;
Here's my book's summary of the responder chain:
http://www.apeth.com/iOSBook/ch11.html#_the_responder_chain
However, there are many other possibilities. As someone has already suggested, you could set up lines of communication by means of NSNotification (shoutcasting); it's ugly, but it does work, and it's intended in part to cover just this sort of tricky situation.
Suppose you implement a custom table view and a custom view controller (which mostly mimics UITableViewControllers behaviour, but when initialized programmatically, ...
#interface Foo : MyCustomTableViewController ...
Foo *foo = [[Foo alloc] init];
... foo.view is kind of class MyCustomTableView instead of UITableView:
// MyCustomTableView.h
#protocol MyTableViewDelegate <NSObject, UITableViewDelegate>
// ...
#end
#protocol MyTableViewDataSource <NSObject, UITableViewDataSource>
// ...
#end
#interface MyCustomTableView : UITableView
// ...
#end
// MyCustomTableViewController.h
#interface MyCustomTableViewController : UIViewController
// ...
#end
How should you implement/override init methods in correct order/ways so that you could create and use an instance of MyCustomTableView both by subclassing MyCustomTableViewController programmatically or from any custom nib file by setting custom class type to MyCustomTableView in Interface Builder?
It important to note that this is exactly how UITableView (mostly UIKit for that matter) works right now: a developer could create and use either programmatically or by creating from nib, whether be it File owner's main view or some subview in a more complex hierarchy, just assign data source or delegate and you're good to go...
So far I managed to get this working if you subclass MyCustomTableViewController, where I will create an instance of MyCustomTableView and assign it to self.view in loadView method; but couldn't figure out how initWithNibName:bundle:, initWithCoder:, awakeFromNib, awakeAfterUsingCoder:, or whatever else operates. I am lost in life cycle chain and end up with a black view/screen each time.
Thanks.
It is a real mystery how the UITableViewController loads its table regardless of if one is hooked up in interface builder, however I have came up with a pretty good way to simulate that behavior.
I wanted to achieve this with a reusable view controller that contains a MKMapView, and I figured out a trick to make it happen by checking the background color of the view.
The reason this was hard is because any call to self.view caused the storyboard one to load or load a default UIView if didnt exist. There was no way to figure out if inbetween those 2 steps if the user really didn't set a view. So the trick is the one that comes from a storyboard has a color, the default one is nil color.
So now I have a mapViewController that can be used in code or in storyboard and doesn't even care if a map was set or not. Pretty cool.
- (void)viewDidLoad
{
[super viewDidLoad];
//magic to work without a view set in the storboard or in code.
//check if a view has been set in the storyboard, like what UITableViewController does.
//check if don't have a map view
if(![self.view isKindOfClass:[MKMapView class]]){
//check if the default view was loaded. Default view always has no background color.
if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
//switch it for a map view
self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
self.mapView.delegate = self;
}else{
[NSException raise:#"MapViewController didn't find a map view" format:#"Found a %#", self.view.class];
}
}
The strategy I've used when writing such classes has been to postpone my custom initialization code as late as possible. If I can wait for viewDidLoad or viewWillAppear to do any setup, and not write any custom code in init, initWithNibName:bundle: or similar methods I'll know that my object is initialized just like the parent class no mater what way it was instantiated. Frequently I manage to write my classes without any overrides of these init methods.
If I find that I need to put my initialization code in the init methods my strategy is to write just one version of my initialization code, put that in a separate method, and then override all the init methods. The overridden methods call the superclass version of themselves, check for success, then call my internal initialization method.
If these strategies fail, such that it really makes a difference what way an object of this class is instantiated, I'll write custom methods for each of the various init methods.
This is how I solved my own issue:
- (void)loadView
{
if (self.nibName) {
// although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
[super loadView];
}
else {
self.view = // ... whatever UIView you'd like to create
}
}