I am extremely new to objective-c and iPhone programming (although, i have a little more background with C#), and I am learning by doing.
I am currently trying to make a mini platform game and instead of checking each platform on its own to see if my player intersects with it, I want to make an array and a for statement which will take care of that. (Correct me if i am wrong but NSMutableArray seems a lot like the List feature in C#)
I typed what i think would work but it hasn't, any ideas why? In my #interface I have:
#interface ViewController : UIViewController
{
NSMutableArray *platforms;
UIImageView *platform1;
UIImageView *platform2;
UIImageView *platform3;
UIImageView *platform4;
UIImageView *platform5;
UIImageView *player;
}
#property (nonatomic) NSInteger GameState;
#property IBOutlet UIImageView *player;
#property IBOutlet UIImageView *platform1;
#property IBOutlet UIImageView *platform2;
#property IBOutlet UIImageView *platform3;
#property IBOutlet UIImageView *platform4;
#property IBOutlet UIImageView *platform5;
And in my #implementation i have:
- (void)viewDidLoad
{
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:1.0/60 target:self selector:#selector(gameLoop) userInfo:nil repeats:YES];
gravity = CGPointMake(0,0.195);
[platforms addObject:platform1];
[platforms addObject:platform2];
[platforms addObject:platform3];
[platforms addObject:platform4];
[platforms addObject:platform5];
}
- (void)gameLoop
{
playerVelocity = CGPointMake(playerVelocity.x,playerVelocity.y + gravity.y);
player.center = CGPointMake(player.center.x + playerVelocity.x,player.center.y + playerVelocity.y);
for(UIImageView *platform in platforms)
{
if(CGRectIntersectsRect(platform.frame,player.frame))
{
BOOL check = YES; //break point here to check if it reaches this point
}
}
}
Also, when i simply type:
if(CGRectIntersectsRect(platform1.frame,player.frame))
{
BOOL doubleCHECK = YES;
}
It works.
You failed to allocate your platforms array. All objects in objective-c are pointers, therefore, probably in your viewDidLoad method, you need a line of code like this:
platforms = [[NSMutableArray alloc] init];
If you check platforms before the loop, I believe you will find that it is nil. You need to create it before you use it (viewDidLoad is probably the best place, unless you need it before the view is loaded) - and unlike some languages, operating on a null object will silently return 0, not throw an exception.
Related
Manual memory management is used. The following code runs well and no crash occurs. But there is no -(void)dealloc method. Is this code wrong? Should I add -(void)dealloc?
MyClass.h
#import <UIKit/UIKit.h>
#interface MyClass : NSObject {
#private
BOOL flag;
UIView *view;
UILabel *label;
UIButton *button;
UITabBar *tabBar;
UIWebView *webView;
UIImageView *imageView;
}
#property (nonatomic, retain) UIView *view;
#property (nonatomic, retain) UILabel *label;
#property (nonatomic, retain) UIButton *button;
#property (nonatomic, retain) UITabBar *tabBar;
#property (nonatomic, retain) UIWebView *webView;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize view;
#synthesize label;
#synthesize button;
#synthesize tabBar;
#synthesize webView;
- (id)init {
self = [super init];
if (self) {
// Initialization code
// Among other code,
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
}
// Other methods here.
// But -(void)dealloc is not overridden here in the MyClass.m
#end
If we must add -(void)dealloc for the above code, should it be like this:
Overridden -(void)dealloc
-(void)dealloc {
[view release];
[label release];
[button release];
[tabBar release];
[webView release];
[super dealloc];
}
Update 1
#synthesize added, see above.
Update 2
Didn't put this into another post because this seems rather related issue:
See the above MyClass.m/.h, there is a private ivar (not sure it should be called ivar or field here) UIImageView *imageView;, it has no property for it, no #synthesize, initialization given there, how can we dealloc it? Also [imageView release]; in -(void)dealloc?
Update 3
Do we have to check availability before releasing ivars? That is, instead of [view release];, use this:
if (nil != view) {
[view release];
}
Yes. You need to implement dealloc.
You dealloc will look like :
-(void)dealloc {
[_view release];
[_label release];
[_button release];
[_tabBar release];
[_webView release];
[super dealloc];
}
Any retained/copy property should be released on dealloc.
Your iVar have no meaning. They do not have the same information as the properties, so you can remove your iVars.
If you want your properties to be backed up by your iVars you should #synthesize them like:
#synthesize view = view;
#synthesize label = label;
#synthesize button = button;
#synthesize tabBar = tabBar;
#synthesize webView = webView;
As you are not using ARC, your code (including the dealloc method) is correct, however you are missing the #synthesize statement in the implementation for the properties to work:
#implementation MyClass
// this will synthesize the getters and setters
#synthesize view, label, button, tabBar, webView;
- (id)init {
self = [super init];
if (self) {
// Initialization code
}
return self;
}
// Other methods here.
-(void)dealloc {
[view release];
[label release];
[button release];
[tabBar release];
[webView release];
[super dealloc];
}
#end
While Daniel's answer is correct in addressing your question, it does not cover what you should do.
This is how your code would be written in a modern world:
turn on ARC. Especially if you are learning or this is your first project. There is no reason to not use ARC. Learning manual-retain-release is valuable, but not critical at this time as the tools do a very good job of providing analysis of when ARC based patterns are leaking memory (either the analyzer or using Instruments, both of which you would need to use under MRR and neither of which work as well under MRR).
Don't use #synthesize and don't declare iVars (and certainly don't declare iVars in your .h file). Let the compiler autho-synthesize the _ prefixed ivars automatically. The _ prefix has the added advantage of disallowing you from accidentally directly accessing an ivar in code. I.e. self.foo vs. foo; the second won't compile.
Or to translate into code:
#interface MyClass : NSObject
#property (nonatomic, retain) UIView *view;
#property (nonatomic, retain) UILabel *label;
#property (nonatomic, retain) UIButton *button;
#property (nonatomic, retain) UITabBar *tabBar;
#property (nonatomic, retain) UIWebView *webView;
#end
And:
#implementation MyClass
{
BOOL _flag;
UIImageView *_imageView;
}
- (id)init {
self = [super init];
if (self) {
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
}
// no -dealloc
#end
Note that I declared _imageView as an instance variable that will be compatible with the obvious imageView #property should you need to refactor your class later to make that externally available.
If you realy must use manual retain-release, then add a -dealloc method that calls -release on all the ivars. I.e.[_view release];, [_imageView release];, etc...
Don't get what you mean by "Note that I declared _imageView as an
instance variable that will be compatible with the obvious imageView
#property should you need to refactor your class later to make that
externally available."
If you were to decide that _imageView needs to be accessible to other objects, then you would delete the iVar declaration and add:
#property(nonatomic, retain) UIImageView *imageView;
The compilers automatic synthesis would create an instance variable named _imageView automatically and none of the rest of your code would have to change.
Do we have to make sure an ivar is not nil before releasing it in
dealloc method? (See Update 3 above.)
No. In Objective-C, nil eats messages. That is, [nil release]; is perfectly valid and does absolutely nothing at runtime.
In your code the BOOL flag; has disappeared. Do we have make a
property for BOOL flag;, that is, #property BOOL flag;? Or all we have
to do is just placing a private field in the MyClass.h as #private
BOOL flag; up there in my original question?
I forgot it. You could create a property for it. Or you could declare _flag as an iVar next to _imageView as I've done above.
Most importantly, there is no longer any reason (save for a very rare case that is generally too be avoided) to declare instance variables in your .h.
I'm very new to programming and objective c so please go easy on me.
I would like a UIButton (which I'm using as an IBAction) to change an the image in a UIImageView when pressed. I put the UIImageView into a UIScrollView in the storyboard but I hope I can still programmatically change the image.
I have searched absolutely everywhere for an answer for hours but nothing has worked for me either because it wasn't the right solution or I didn't do it properly.
I have tried this code and some more but they always return a "Thread 1: signal SIGABRT" when I press the button:
imageView.image = [UIImage imageNamed: #"Ruler pic inch.png"];
Here is my code so far:
ViewController.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface ViewController : UIViewController <ADBannerViewDelegate, UIScrollViewDelegate> {
ADBannerView *adView;
BOOL bannerIsVisible;
IBOutlet UIScrollView *scrollView;
IBOutlet UIImageView *imageView;
IBOutlet UIButton *proVersion;
IBOutlet UIButton *howToUse;
}
- (IBAction)switchUnit:(id)sender;
#property (nonatomic, assign) BOOL bannerIsVisible;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize bannerIsVisible;
#synthesize scrollView;
#synthesize imageView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Hide status bar:
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// iAd:
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, -50.0f);
[self.view addSubview:adView];
adView.delegate=self;
self.bannerIsVisible=NO;
// Setting scrollview content size to size of image
scrollView.contentSize = CGSizeMake(320,2246);
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, 50.0f);
[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, -50.0f);
[UIView commitAnimations];
self.bannerIsVisible = NO;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)switchUnit:(id)sender {
// CODE THAT MAKES IMAGE CHANGE GOES HERE (I thought...)
}
#end
Help would be greatly appreciated. I'm fed up with it. I've probably done a silly mistake somewhere but I can't work it out. Thanks in advance.
General pointers:
Try to use #property for all your instance variables (it'll server you well in future)
Don't use #synthesize (the compiler does a better job of it for you)
If you have a property bob, access it by using self.bob
Turn on ARC (looks like you don't have it on currently)
Best not to have any spaces in image names
To be clear on properties, when you have a property you don't need to create instance variables in between {} of the #interface. These should be removed in order to prevent multiple definitions. The auto-synthesising done by the compiler will define the instance variables for you but you should always use the property to access the value (as in item 3 above).
That said, your - (IBAction)switchUnit:(id)sender looks fine, if a little empty. The code should be something like:
self.imageView.image = [UIImage imageNamed:#"Ruler_pic_inch.png"];
For it to work, the image Ruler_pic_inch.png would need to be in your bundle (which means it's in Xcode project and set to copy during the build.
Again, that said, your problem would seem to relate to you being confused somewhere. SIGABRT type issues would usually be picked up by the compiler and notified to you as a warning saying something like "some object may not respond to some selector". If you see any of those, look at them and work out why you're trying to ask the object a question it doesn't understand.
If you don't see any compiler warnings, then you've told the compiler that an object is going to be of one type and then actually set it to something else. In this case I'd look at your IBOutlets in Storyboard and check that they are actually set to the correct destination view (disconnect and reconnect them all to be sure).
I'm confused about below situation.
I have a viewcontroller(VC), it has 1 subview(SubV) and 1 other class.(classA)
Also i have an event handler called from classA, i want this event handler to change my subV in VC.
When i access SubV from VC directly, it is OK, image of subview changed etc.
But when the classA triggers an event handler of VC, it reaches VC, also access subView's method but no change in my subView !!! (I also try delegate but the result is same)
ViewController.h
#interface ViewController : UIViewController {
.
IBOutlet SubView *subView;
ClassA *classA;
.
}
#property (retain, nonatomic) IBOutlet SubView *subView;
#property (retain, nonatomic) ClassA *classA;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.subView = [self.subView init];
self.classA = [[ClassA alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(eventListener:) name:#"eventType" object:nil];
}
- (void) eventListener:(NSNotification *) not
{
[self.subView RefreshView]; // it doesnt work! calls refreshView method but no change
}
- (IBAction)buttonPressed:(id)sender
{
[self.subView RefreshView]; // it works perfect
}
SubView.h
#interface SubView : UIImageView
#property int state;
#property NSArray *imageArray;
- (void) RefreshView;
- (id) init;
#end
SubView.m
- (void) RefreshView{
[self stopAnimating];
self.imageArray = [NSArray arrayWithObjects:[UIImage imageNamed:#"a.png"], nil];
self.animationDuration = 1;
self.animationImages = self.imageArray;
self.animationRepeatCount = 0;
[self startAnimating];
}
ClassA.m
-(void)methodA{
[myEvent requestEvent];
}
So, what i am trying to do here is accessing & changing subView with a button in Viewcontroller and with a thread running in another classA
Edit: Not quite sure what to make of this. You've edited your post to the changes I proposed, making my answer look superfluous at best and deranged at worst...
You have created the class SubView as a subclass of UIImageView. But the IBOutlet *subView is not a member of class SubView but a member of UIImageView. I suspect this might carry over to the xib/storyboard as well(?) If so, this means that any messages you send to your subView instance will be handled by a stock UIImageView rather than your own class...
#property (retain, nonatomic) IBOutlet UIImageView *subView;
should probably read
#property (retain, nonatomic) IBOutlet SubView *subView;
Finally i've found the solution! Because of the thread that i run in ClassA, i should use '
[self performSelectorOnMainThread:#selector(myMethodToRefreshSubView) withObject:nil waitUntilDone:NO];
in my eventListener method.
I am working on a Mac application. One of the windows can load several NSView objects that are in the same NIB/XIB file.
But my code looks like this:
#interface TheWindowController : NSWindowController {
//Interface objects
IBOutlet NSTableView *detailsTree;
IBOutlet NSView *bigView;
IBOutlet NSView *subView1;
IBOutlet NSView *subView2;
IBOutlet NSView *subView3;
IBOutlet NSView *subView4;
IBOutlet NSView *subView5;
}
My question is if that is possible to hold all these IBOutlets inside an Array, Dictionary or something alike. So in the future I could do something like this in my implementation:
- (IBAction)traceTableViewClick:(id)sender {
//having now a NSArray called subviewsArray
[[[bigView subviews] objectAtIndex:0] removeFromSuperview];
[rightView addSubview: [subviewsArray objectAtIndex:[detailsTree selectedRow]]];
}
Is it possible? How? Any examples?
I have done something similar with a custom view that contains many controls that I want to manipulate en masse. You need to keep them separate in the #interface declaration so that the IBOutlet property works correctly with Interface Builder, but in your init method you can organize them into an array or NSArray yourself:
- (id)init
{
self = [super init];
if (self != nil)
{
_viewArray = [[NSArray alloc] initWithObjects:subView1, subView2,
subView3, subView4, subView5, nil];
}
return self;
}
- (void)dealloc
{
[_viewArray release];
...etc...
[super dealloc];
}
You can then work on them as you desire:
- (void)doThing
{
for (id view in _viewArray)
{
[view doSomething];
}
}
Just add your NSViews to a NS(Mutable)Array or a NS(Mutable)Dictionary the same way you would add any other object to them.
I have an app where u can move a man with the accelerometer. There is a ready set go sequence and then using pop Animation the guy appears. Although even when the man is not visible and the ready set go words appear the accelerometer is still usable and u can move the guy and accidentally touch the obstacle which then using cgrectintersectsrect changes to An end game screen. So in that ready set go sequence how can I disable the accelerometer then reenable it when the go word appears?
.h:
#interface GameScreen : UIViewController <UIAccelerometerDelegate> {
IBOutlet UIImageView *image1;
IBOutlet UIImageView *image2;
UIImageView *ball;
CGPoint delta;
IBOutlet UIImageView *man;
IBOutlet UIImageView *fang;
IBOutlet UIImageView* hiddenView;
IBOutlet UILabel* ready;
IBOutlet UILabel* set;
IBOutlet UILabel* go;
IBOutlet UILabel* endScreen;
IBOutlet UIImageView* guy;
CAKeyframeAnimation *popAnimation;
}
#property (nonatomic, retain)UIImageView *image1;
#property (nonatomic, retain)UIImageView *image2;
-(void)checkCollision;
#property (nonatomic, retain)IBOutlet UIImageView *ball;
#property CGPoint delta;
#end
.m:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
NSLog(#"x : %g", acceleration.x);
NSLog(#"y : %g", acceleration.y);
NSLog(#"z : %g", acceleration.z);
delta.y = acceleration.y * 50;
delta.x = acceleration.x * 50;
ball.center = CGPointMake(ball.center.x + delta.x, ball.center.y + delta.y);
// Right
if(ball.center.x < 0) {
ball.center = CGPointMake(320, ball.center.y);
}
// Left
if(ball.center.x > 320) {
ball.center = CGPointMake(0, ball.center.y);
}
// Top
if(ball.center.y < 0) {
ball.center = CGPointMake(ball.center.x, 460);
}
// Bottom
if(ball.center.y > 460){
ball.center = CGPointMake(ball.center.x, 0);
}
[self checkCollision];
}
You really should be reading a good book on coding and doing some tutorials to get an idea of how to do this, but the concept of a flag is simple.
In the class where your man is moved have a bool called bCanMove or similar, and set that to false when the class is initialised.
Once your "ready, steady, go" sequence finishes you set that to true.
The next step is check this flag in the code where you perform your movement, obviously I can't see your code but I expect you've got a method with a name similar to didAccelerate — in there just check to see if the boolean is true or not, and don't do anything if it isn't, for example:
if(!bCanMove)
{
return;
}
As I can't see your code I don't know whether you're relying on inbuilt mechanisms etc., but as other people have stated (and myself) you shouldn't really be asking these kinds of questions on here — not because you're new to coding but more the way you've phrased the question, nobody can really help without any details and it's something you would't need to ask if you did a little more reading ;)
At a bare minimum you should read this: http://cocoadevcentral.com/d/learn_objectivec/ but that's not going to help you if you don't understand some basic programming concepts (and it would appear you don't) — for that there is no substitute for a good book/tutorial.