I've been experiencing some difficulties with particle effects. Originally I posted a question about how the particle effects in my app were different when on an ios6 device.
Particle system looks different on iOS 6 and ios7
I Originally designed the app on an ios7 device and got the particles looking like I wanted, but on the ios6 device the particles were smaller and displaced.
I was told it was problem between ios6 and ios7 and that changing the media timing could fix it.
Ive tried everything, but nothing has worked.
Eventually I tried just creating a separate app and pasted in the code from this tutorial - http://weblog.invasivecode.com/post/45058779586/caemitterlayer-and-the-ios-particle-system-lets in the view did load. The particles work as they should, but when I put the same code into my current apps viewDidLoad. The particles were all over the place and not doing what they should be.
I'm wandering if there is something in my current app that Im missing, that is upsetting the particles. Can anyone suggest things to look for?
Originally the particle effects were subclassed and being called after adding a subview to my main view.
Here is my view controller .h code (I've taken out some irrelevant control object properties and outlets)
#interface HHViewController : UIViewController < HHScoreViewdelegate>
{
IBOutlet UIButton* btnStart;
IBOutlet UIButton* btnStop;
IBOutlet UIButton* btnMore;
IBOutlet UIButton* btnHelp;
IBOutlet UIButton* btnTessilaCoil;
IBOutlet UIImageView* imgAnimatedTessila;
IBOutlet UIImageView* imgBubble;
IBOutlet UIImageView* imgSteam;
IBOutlet UIImageView* imgMachineLightsTop;
IBOutlet UIImageView* imgBackground;
NSTimer* StopSignTimer;
NSTimer* timer;
NSTimer* lightMachineTimer;
NSTimer* ComputerLightsTimer;
CGFloat AnimationDelay;
AVAudioPlayer* audioPlayer;
AVAudioPlayer* audioPlayer2; //Used for game end audio, so that it doesnt interupt last letter/number audio
int pageValue;
IBOutlet UIButton* btnSetting;
IBOutlet UIButton* btnIAP;
IBOutlet UIButton* btnNext;
IBOutlet UIButton* btnForward;
IBOutlet UIImageView* imgLightMachine;
IBOutlet UIImageView* imgComputerLights;
NSString* strletterName;
HHScoreView * scoreSelection;
More * MorePanelView;
InAppPurchase * InAppPurchaseView;
NSMutableArray* ButtonBackgroundImages;
NSMutableArray* AlphabetBubbles;
NSMutableArray* SavedBubblePositions;
NSTimer* bubbleBlinkingTimer;
int wrongbubbleTapCount;
IBOutlet UIImageView* imgAlphabetDisplay;
IBOutlet UILabel* lblNextOneToPop;
// Code from Particle Tutorial
CAEmitterLayer *_myEmitter;
CAEmitterCell *_myCell;
// End code form Particle Tutorial
}
// Code from Particle Tutorial
#property(nonatomic, retain) CAEmitterLayer *_myEmitter;
#property(nonatomic, retain) CAEmitterCell *_myCell;
// End code form Particle Tutorial
#property(nonatomic,strong) UILabel* lblNextOneToPop;
#property(nonatomic,strong) NSTimer* SteamTimer;
#property(nonatomic,strong) NSTimer* StopSignTimer;
#property(nonatomic,strong) NSTimer* timer;
#property(nonatomic,strong) NSTimer* lightMachineTimer;
#property(nonatomic,strong) NSTimer* ComputerLightsTimer;
#property(nonatomic,retain)IBOutlet UIButton* btnStart;
#property(nonatomic,retain)IBOutlet UIButton* btnStop;
#property(nonatomic,retain)IBOutlet UIButton* btnMore;
#property(nonatomic,retain)IBOutlet UIButton* btnHelp;
#property(nonatomic,retain)IBOutlet UIButton* btnSetting;
#property(nonatomic,retain)IBOutlet UIButton* btnTessilaCoil;
#property(nonatomic,retain)IBOutlet UIImageView* imgAnimatedTessila;
#property(nonatomic,retain)IBOutlet UIImageView* imgSteam;
#property(nonatomic,retain)IBOutlet UIImageView* imgMachineLightsTop;
#property(nonatomic,retain)IBOutlet UIImageView* imgBackground;
#property(nonatomic,retain)IBOutlet UIScrollView* scScrollView;
#property(nonatomic,retain)IBOutlet IBOutlet UIButton* btnNext;
#property(nonatomic,retain)IBOutlet IBOutlet UIButton* btnForward;
#property(nonatomic,retain)IBOutlet UIImageView* imgLightMachine;
#property(nonatomic,retain)IBOutlet UIImageView* imgComputerLights;
#property(nonatomic,retain)NSString* strletterName;
#property(assign)int allLettersEventCount;
#property(nonatomic,retain) NSArray* ButtonBackgroundImages;
#property Boolean StopButtonClicked;
#property Boolean BubbleAOnscreen;
#property (nonatomic,assign) BOOL cancelAll;
#property int pageValue;
#property int positionRotation;
-(IBAction)tapPiece:(UITapGestureRecognizer *)recognizer;
-(IBAction)actionStartSign:(id)sender;
-(IBAction)actionStopSign:(id)sender;
-(IBAction)actionMoreSign:(id)sender;
-(IBAction)actionHelpSign:(id)sender;
-(IBAction)actionTessilaCoil:(id)sender;
-(IBAction)actionbtnHelp:(id)sender;
-(IBAction)actionbtnSetting:(id)sender;
-(IBAction)actionbtnIAP:(id)sender;
+(HHViewController*)sharedManager;
- (IBAction)purchaseItem:(id)sender;
#property (strong, nonatomic) InAppPurchase *purchaseController;
#end
Here is the code from my viewcontroller .m where I added the code from the tutorial.
#interface HHViewController ()
{
}
#end
#implementation HHViewController
static int OrderingSequence = 0;
#synthesize _myEmitter, _myCell;
/*
TeslarGlowEffect* ShowGlow;
SteamEffect* ShowSteam;
BubbleBurst* PopBubble;
*/
//TeslarTimerSparks* TeslarTimer;
- (void)viewDidLoad
{
[super viewDidLoad];
CFTimeInterval currentTime = [self.view.layer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog(#"Current media Timing = %f", currentTime);
// Code from tutorial
CAEmitterLayer *emitterLayer = [CAEmitterLayer layer]; // 1
emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.origin.y); // 2
emitterLayer.emitterZPosition = 10; // 3
emitterLayer.emitterSize = CGSizeMake(self.view.bounds.size.width, 0); // 4
emitterLayer.emitterShape = kCAEmitterLayerSphere; // 5
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell]; // 6
emitterCell.scale = 0.1; // 7
emitterCell.scaleRange = 0.2; // 8
emitterCell.emissionRange = (CGFloat)M_PI_2; // 9
emitterCell.lifetime = 5.0; // 10
emitterCell.birthRate = 10; // 11
emitterCell.velocity = 200; // 12
emitterCell.velocityRange = 50; // 13
emitterCell.yAcceleration = 250; // 14
emitterCell.contents = (id)[[UIImage imageNamed:#"Steam1.png"] CGImage]; // 15
emitterLayer.emitterCells = [NSArray arrayWithObject:emitterCell]; // 16
[self.view.layer addSublayer:emitterLayer]; // 17
//end code from tutorial
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appWillEnterForegroundNotification:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appWillEnterBackgroundNotification:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[btnStart addTarget:self action:#selector(actionStartSign:) forControlEvents:UIControlEventTouchUpInside];
//NSLog(#"OrderingSequence = %d",OrderingSequence );
imgAnimatedTessila.image = [UIImage imageNamed:KTESSILAIMAGE];
self.alphebetindex = 0;
wrongbubbleTapCount = 0;
//// Load a bunch of arrays used in the game.
[btnHelp addTarget:self action:#selector(actionbtnHelp:) forControlEvents:UIControlEventTouchUpInside];
[btnSetting addTarget:self action:#selector(actionbtnSetting:) forControlEvents:UIControlEventTouchUpInside];
NSString* strFirstTimeloaded = [[NSUserDefaults standardUserDefaults]valueForKey:#"FirstTime"];
if(strFirstTimeloaded == nil)
{
// Show preference page if first time used.
[[NSUserDefaults standardUserDefaults]setValue:#"English" forKey:#"Language"];
LanguagesSelectView* selection = [[LanguagesSelectView alloc]initWithFrame:CGRectMake(0, 0, 1024, 748)];
[self.view addSubview:selection];
[[NSUserDefaults standardUserDefaults]setValue:#"FirstTime" forKey:#"FirstTime"];
}
positionRotation = 0;
self.strletterName = #"29";
self.allLettersEventCount = 0;
[[NSUserDefaults standardUserDefaults] setValue:#"One" forKey:#"Mode"];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(enableSetting) name:#"EnableSetting" object:nil];
//[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(LetterTapped:) name:#"LetterTapped" object:nil];
btnStop.hidden = YES;
btnNext.hidden = YES;
btnForward.hidden = YES;
[self addGestureRecognizersToView];
//Create timer for release of steam from bubble machine
SteamTimer = [NSTimer scheduledTimerWithTimeInterval:5.2 target:self selector:#selector(ShowSteamEffect) userInfo:nil repeats:YES];
//Set Font for lblNextOneToPop
[lblNextOneToPop setFont:[UIFont fontWithName:#"SF Slapstick Comic" size:110]];
lblNextOneToPop.hidden = YES;
}
Here is one of my particle effects that was working on my ios7 iPad, but on the ios6 iPad, the effect looked totally different.
It was after long hrs trying to figure out why the ios7 and ios6 looked different that I tried the exercise of putting the tutorial code into a new app and then it into my own app. In my app the particles are totally different, but as they should be in the new app.
With respect to #matt's comments below. I know there must be something going on with my app, but I can't see it. I've looked all over the web and all through my code and I think I'm missing something.
All I want are three particles effects that appear at specific locations regardless of iOS version. One of them on user touch. The effect below is just one of them. I've tried the CAmediaTiming thing, but that didn't work.
SteamEffect.h
#import <UIKit/UIKit.h>
#interface SteamEffect : UIView
#end
The steamEffect.m looks like this.
#import "SteamEffect.h"
#implementation SteamEffect
{
CAEmitterLayer* SteamEmitter;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
SteamEmitter = (CAEmitterLayer*) self.layer;
//SteamEmitter.emitterPosition= CGPointMake(0, 0);
//SteamEmitter.emitterSize = CGSizeMake(10,10);
CAEmitterCell* Steam = [CAEmitterCell emitterCell];
Steam.birthRate = 50.0f;
Steam.spin = .6f;
Steam.lifetime = 1.7f;
Steam.alphaRange = 0.2f;
Steam.alphaSpeed = 0.2f;
Steam.contents = (id)[[UIImage imageNamed:#"Steam1.png"]CGImage];
Steam.velocity = 30;
Steam.velocityRange = 50.0f;
Steam.emissionLongitude = -60.0f;
Steam.emissionRange = M_1_PI;
Steam.scale = .2f;
Steam.scaleSpeed = .5f;
Steam.yAcceleration = -200.0f;
//SteamEmitter.renderMode = kCAEmitterLayerBackToFront;//kCAEmitterLayerAdditive;
//SteamEmitter.emitterShape = kCAEmitterLayerCircle;
SteamEmitter.emitterCells = #[Steam];
SteamEmitter.emitterPosition = CGPointMake(815, 445);
//Check System Version
NSArray *vComp = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ([[vComp objectAtIndex:0] intValue] == 7) {
// iOS-6 code
SteamEmitter.beginTime = CACurrentMediaTime();
}
}
return self;
}
-(void)didMoveToSuperview
{
//1
[super didMoveToSuperview];
if (self.superview==nil) return;
[self performSelector:#selector(disableEmitterCell) withObject:nil afterDelay:0.5];
}
-(void)disableEmitterCell
{
[SteamEmitter setValue:#0 forKeyPath:#"birthRate"];
}
+ (Class) layerClass
{
//tell UIView to use the CAEmitterLayer root class
return [CAEmitterLayer class];
}
- (void) setEmitterPosition:(CGPoint)pos
{
SteamEmitter.emitterPosition = pos;
}
- (void) toggleOn:(bool)on
{
//SteamEmitter.birthRate = on? 300 : 0;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
I called the above from my viewcontroller .m using the following. There is also a timer in the viewDidLoad above that calls this every 5 sec. Although that is probably not the way to do it. Probably better to add the view and start it emitting every 5 sec rather than adding a new view each time, but thats a different issue.
- (void)ShowSteamEffect
{
//Check System Version because NSAttributedString only works in ios6 and above.
NSArray *vComp = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
//Create view for Steam particle effect.
//CGRect SteamFrame = CGRectMake(790, 380, 100, 100); //ORIGINAL
CGRect SteamFrame = CGRectMake(0, 0, 0, 0); //(815, 455, 100, 100)
//Show Steam effect
ShowSteam = [[SteamEffect alloc]initWithFrame:SteamFrame];
ShowSteam.hidden = NO;
[self.view insertSubview:ShowSteam aboveSubview:imgComputerLights];
}
Related
I have been doing an Project for IOS which is written in Objective C. Theres a requirement where i have to put a Button or Label below a table view which kind of act as an Dialog like view over an WebView. There is already an header view embedded into the table view, its work well, but when i try to place the button or label below the table view using Storyboard its not working as expected it actually not positioning below the TableView. Below is the image how my View is laid out :
This is how it looks currently :
Below is the code for my view controller :
ViewController.h:
#import "FlatUIKit.h"
#interface NothiViewController : UIViewController
{
IBOutlet UIWebView *mainWebView;
IBOutlet UISegmentedControl *segmentControl;
IBOutlet UIView *viewNothiPermittedUsers;
IBOutlet UILabel *labelTitleNothiPermittedUsers;
IBOutlet UITableView *tableViewNothiPermittedUsers;
}
#property (nonatomic,strong) NSArray *arrNothiPermittedUsers;
#property(nonatomic,retain) ModelNothi *currentModelNothi;
- (void)updateRightBar:(BOOL)all;
#end
#interface CellNothiPermittedUserList : UITableViewCell
{
}
#property (nonatomic, strong) IBOutlet UILabel *labelUserName;
#property (nonatomic, strong) IBOutlet FUIButton *buttonSend;
#end
ViewController.m:
#interface NothiViewController ()<UIWebViewDelegate,CustomAlertViewDelegate>
{
RequestNothiActionApiType currentNothiActionApiType;
AlertType currentAlertType;
}
#property(nonatomic,strong)NSData *dataNotangsho;
#property(nonatomic,strong)NSData *dataPotrangsho;
#end
#implementation NothiViewController
#synthesize currentModelNothi;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = APP_VIEW_BACKGROUND_COLOR;
mainWebView.delegate = self;
mainWebView.backgroundColor = UIColor.clearColor;
mainWebView.opaque = YES;
mainWebView.scalesPageToFit = YES;
UIFont *font = [UIFont systemFontOfSize:16.0f];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:font
forKey:NSFontAttributeName];
[segmentControl setTitleTextAttributes:attributes
forState:UIControlStateNormal];
segmentControl.tintColor = COLOR_LOGIN_BUTTON;
self.dataNotangsho = nil;
self.dataPotrangsho = nil;
currentNothiActionApiType = API_TYPE_NOTANGSHO;
NSDictionary *params = #{#"data_ref": #"api",
#"api_key": API_KEY,
#"user_designation": [AppSupporter sharedInstance].currentDesignationID};
NSString *api = [NSString stringWithFormat:#"%#/%#",API_NOTHI_NOTANGSHO,self.currentModelNothi.nothiParts];
CGRect frame = segmentControl.frame;
frame.origin.y = NAV_BAR_HEIGHT + STATUS_BAR_HEIGHT;
segmentControl.frame = frame;
segmentControl.backgroundColor = APP_VIEW_BACKGROUND_COLOR;
// Delay execution of my block for 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self connectServer:api withParams:params withProgressMessage:#"তথ্য লোড হচ্ছে, একটু অপেক্ষা করুন..."];
});
viewNothiPermittedUsers.hidden = YES;
viewNothiPermittedUsers.backgroundColor = [UIColor colorWithWhite:0.0F alpha:0.7f];
labelTitleNothiPermittedUsers.backgroundColor = [UIColor lightGrayColor];
[labelTitleNothiPermittedUsers.layer setCornerRadius:8.0f];
labelTitleNothiPermittedUsers.layer.masksToBounds = YES;
labelTitleNothiPermittedUsers.font = [UIFont boldFlatFontOfSize:16.0f];
tableViewNothiPermittedUsers.backgroundColor = [UIColor whiteColor];
tableViewNothiPermittedUsers.tableFooterView = [[UIView alloc] initWithFrame : CGRectZero];
[tableViewNothiPermittedUsers.layer setCornerRadius:8.0f];
tableViewNothiPermittedUsers.layer.masksToBounds = YES;
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(notifyNothiForward:) name:EventNothiForward object:nil];
}
Any help would be highly appreciated. Note i am kind of new to IOS programming and the project was written by someone else, which i just got now to extend it, so i am little aware of positioning views at the moment.
first question so take it easy!
Creating a simple iOS App. I have a view which contains a rotating dial (a UIImageView) (rotates through Rotation Gesture) and a UIButton which when pressed uses UIView animateWithDuration to move the button to one of 3 locations.
My issue.. rotating the dial is fine, moving the button is fine, BUT.. when I move the button, then rotate the dial, the location of the button is "reset" to its original frame.
How can I stop the rotation effecting the location of the button / the movement of the button.
ViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>{
IBOutlet UIImageView *lock;
IBOutlet UILabel *number;
IBOutlet UILabel *displayNumber1;
IBOutlet UILabel *displayNumber2;
IBOutlet UILabel *displayNumber3;
IBOutlet UIButton *slider;
AVAudioPlayer *theAudio;
}
#property (retain, nonatomic) IBOutlet UIImageView *lock;
#property (retain, nonatomic) IBOutlet UILabel *number;
#property (retain, nonatomic) IBOutlet UILabel *displayNumber1;
#property (retain, nonatomic) IBOutlet UILabel *displayNumber2;
#property (retain, nonatomic) IBOutlet UILabel *displayNumber3;
#property (retain, nonatomic) IBOutlet UIButton *slider;
#property (retain, nonatomic) AVAudioPlayer *theAudio;
-(IBAction)moveSlider:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
#synthesize lock, number, slider, displayNumber1, displayNumber2, displayNumber3, theAudio;
CGFloat _lastRotation;
int lastNumber;
NSTimer *clickTimer;
int _whichNumberSelected;
int _currentDirection;
CGFloat _changeDirectionMatch;
float no1Left = 61;
float no2Left = 151;
float no3Left = 238;
NSString *lastText1;
CGRect newFrameSet;
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
lock.userInteractionEnabled = YES;
UIRotationGestureRecognizer *rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotationWithGestureRecognizer:)];
rotationGestureRecognizer.delegate = self;
[lock addGestureRecognizer:rotationGestureRecognizer];
_lastRotation = 0;
lastNumber = 0;
NSString *path = [[NSBundle mainBundle] pathForResource:#"click" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:NULL];
theAudio.volume = 1.0;
[theAudio prepareToPlay];
_currentDirection = 0;
_changeDirectionMatch = 0;
_whichNumberSelected = 1;
lastText1 = ([NSString stringWithFormat:#"0"]);
}
-(IBAction)moveSlider:(id)sender {
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGRect newFrame = slider.frame;
switch (_whichNumberSelected) {
case 1:
newFrame.origin.x= no2Left;
_whichNumberSelected = 2;
break;
case 2:
newFrame.origin.x= no3Left;
_whichNumberSelected = 3;
break;
case 3:
newFrame.origin.x= no2Left;
_whichNumberSelected = 4;
break;
case 4:
newFrame.origin.x= no1Left;
_whichNumberSelected = 1;
break;
default:
break;
}
slider.frame = newFrame;
newFrameSet = newFrame;
} completion:nil];
}
-(void)handleRotationWithGestureRecognizer:(UIRotationGestureRecognizer *)rotationGestureRecognizer{
CGFloat newRot = RADIANS_TO_DEGREES(rotationGestureRecognizer.rotation) / 3.6;
lock.transform = CGAffineTransformRotate(lock.transform, rotationGestureRecognizer.rotation);
CGFloat c = _lastRotation - newRot;
_lastRotation = c;
if(c < 0){
c = c + 100;
_lastRotation = c;
}
if(c >= 100){
c = c-100;
_lastRotation = c;
}
if(c >= 99.5){
displayNumber1.text = #"0";
} else {
displayNumber1.text = ([NSString stringWithFormat:#"%.f", c]);
}
if([displayNumber1.text isEqualToString:lastText1]){
} else {
[theAudio play];
}
lastText1 = displayNumber1.text;
rotationGestureRecognizer.rotation = 0.0;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Welcome to Stack Overflow.
My guess is that your view controller has AutoLayout active. If that's the case, you need to change your animation code so that you animate your button by manipulating one or more constraints on it rather than changing it's frame.
The problem is that when you change the frame directly, at some point something triggers a re-layout of the form, and the constraints snap it back into place. Even if you don't explicitly add constraints, the system does.
The rotation animation should be fine since that is changing the rotation on the view's transform, not it's position, and there are no constraints I'm aware of for rotation.
What you do to animate using constraints is to attach constraints to the button in IB, then control-drag from the constraints to your view controller's header to create outlets to the constraints. (Leading and top edge constraints, say.)
Then in your animation change the constant(s) on the constraint(s) and call layoutIfNeeded.
The alternative is to turn off auto-layout for your view controller, use old style "struts and springs" layout, and then your frame animation will work as always.
I'm trying to build a timer app. When the timer hits a certain number (say 5), I want an image to appear. I have the timer working, and the image shows up, but when I declare an if statement to hide the image when the timer has not reached 5, the image shows up anyways. Furthermore, when I declare the UIImageview at the bottom, Xcode reads me the error of "Local declaration of 'images' hides instance variable.
Thank you for any help you can offer! I just started programming.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController;
-(IBAction)Start:(id)sender; {
Timer = [NSTimer scheduledTimerWithTimeInterval:1 target: self selector:#selector(TimerCount) userInfo: nil repeats:YES];
}
-(void)TimerCount{
CountNumber = CountNumber + 1;
TimerDisplay.text=[NSString stringWithFormat:#"%i", CountNumber];
if(CountNumber < 6) {
imagesx.hidden = YES;
}
if(CountNumber > 6){
imagesx.hidden = NO;
}
}
-(IBAction)UIImage:(id) sender{
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Images.jpg"]];
if (CountNumber < 6){
imagesx.hidden = YES;
}
}
-(IBAction)Stop:(id)sender{
[Timer invalidate];
TimerDisplay.text=[NSString stringWithFormat:#"%i", CountNumber];
}
-(IBAction)Reset:(id)sender{
CountNumber = 0;
[Timer invalidate];
CountNumber = 0;
TimerDisplay.text=[NSString stringWithFormat:#"%i", CountNumber];
Timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(TimerCount) userInfo:nil repeats:YES];
[Timer invalidate];
}
-(IBAction)Restart:(id)sender{
CountNumber = 0;
TimerDisplay.text = [NSString stringWithFormat:#"%i", CountNumber];
CountNumber = 0;
Timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(TimerCount) userInfo:nil repeats: YES];
if (CountNumber < 6){
imagesx.hidden = YES;
}
if (CountNumber > 6){
imagesx.hidden = NO;
}
}
- (void)viewDidLoad{
//create UIImage view programmatically
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIImageView *imagesx =[[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
imagesx.image=[UIImage imageNamed:#"images.jpeg"];
[self.view addSubview:imagesx];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
View Controller.h:
#import <UIKit/UIKit.h>
int CountNumber;
#interface ViewController : UIViewController
{
IBOutlet UILabel *TimerDisplay;
UIImageView *imagesx;
NSTimer *Timer;
}
-(void)TimerCount;
-(IBAction)Start:(id)sender;
-(IBAction)Stop:(id)sender;
-(IBAction)Restart:(id)sender;
#property (strong, nonatomic) IBOutlet UILabel *FirstiPhoneapp;
#property (strong, nonatomic) IBOutlet UILabel *ByJuliaTu;
#property (strong, nonatomic) IBOutlet UIButton *Start;
#property (strong, nonatomic) IBOutlet UIButton *Reset;
#property (strong, nonatomic) IBOutlet UIButton *Terminate;
#property (strong, nonatomic) IBOutlet UIButton *Restart;
#end
You declared your image view as an instance variable with the name "imagesx"
#interface ViewController : UIViewController
{
IBOutlet UILabel *TimerDisplay;
UIImageView *imagesx; <-----your ivar
NSTimer *Timer;
}
So now your view controller has a spot in memory to place an image view.
when viewDidLoad: is called you are doing this:
UIImageView *imagesx =[[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
This variable (pointer to a UIImageView) has the same name is the instance variable. This will hide access to that ivar and create a new spot in memory for this local version. In other words when you do this in viewDidLoad:
- (void)viewDidLoad{
//create UIImage view programmatically
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// You are declaring a new image view here with the same name so it "overrides" or hides the ivar by creating a new local one:
UIImageView *imagesx =[[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
// You are assigning your newly created image view to this local variable
imagesx.image=[UIImage imageNamed:#"Matthijs"];
// adding it to the view, which works but:
[self.view addSubview:imagesx];
// the method returns, this local variable is gone and the ivar is still nil
}
// The ivar is never getting a pointer to your newly created image view. You can't access it after the method returns.
if you put a breakpoint in the timerCount method
Your image view was never set to the ivar (it is nil) so you can't get to it in order to set it to hidden.
The fix is simple. Don't redeclare a new image view in viewDidLoad:, just assign it to your ivar:
imagesx = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200,200)];
Or if you do, declare a new one and change the name:
UIImageView* imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50.0f, 50.0f, 200.0f, 200.0f)];
then assign that to your ivar
imagesx = imageView;
Your code is very hard to follow. You shouldn't create ivars anymore, you can just create a proper for the NSTimer and the UIImageView. Also I'm not sure why you created outlets to the buttons if you're just using the IBActions.
In your viewDidLoad you are redeclaring imagesx and adding it as a subview so when you use the ivar in the timer call, you're actually looking at a nil pointer.
You should declare your NSTimer and the UImageView as properties:
#property (strong, nonatomic) NSTimer * timer;
#property (weak, nonatomic) IBOutlet UIImageView * imageView; // Connect this to an ImageView in the storyboard instead of creating it in code
In your .m file you should access the timer and the image as properties:
self.timer =
self.imageView.image = [UIImage imageNamed:#"..."];
I am building a ViewController just to show one image. I added the ImageView programmatically to a scroll view. I would like to allow the user to zoom in and out. This is my code
#interface ImageViewerViewController ()<UIScrollViewDelegate>
#property (nonatomic, strong) UIImageView *ImageView;
#property (weak, nonatomic) IBOutlet UIScrollView *Scroll;
#end
#implementation ImageViewerViewController
-(UIView*) viewForZoomingInScrollView{
return self.ImageView;
}
-(void) viewDidLoad{
self.Scroll.minimumZoomScale = 0.2;
self.Scroll.maximumZoomScale = 1.5;
self.Scroll.delegate = self;
NSLog(#"View did load");
if(self.imageName)
[self updateImage];
}
-(void)setImageName:(NSString *)imageName{
NSLog(#"set Image");
_imageName = imageName;
}
-(void)updateImage{
self.ImageView =[[UIImageView alloc]init];
self.ImageView.image = [UIImage imageNamed:self.imageName];
[self.ImageView sizeToFit];
self.Scroll.contentSize = self.imageName? self.ImageView.image.size: CGSizeZero;
[self.Scroll addSubview:self.ImageView];
}
#end
As you see, I already set the delegate of the scroll to self and I added the protocol header and the needed message.
But the zooming feature is not working.
Could you help me please?
I appreciate your time and efforts.
Regards,
This will work as I created a demo of it. If anything do else let me know.
-(void)viewDidLoad
{
float minimumScale = [_floorPlanImageView frame].size.width /[_floorPlanScrollView frame].size.width;
_floorPlanScrollView.maximumZoomScale = 5; //Change as per you need
_floorPlanScrollView.minimumZoomScale = minimumScale; //Change as you need
_floorPlanScrollView.zoomScale = minimumScale;
_floorPlanScrollView.delegate =self;
_floorPlanScrollView.clipsToBounds = YES;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _floorPlanImageView;
}
I'm struggling with a retaining issue between two of my UIViewControllers. The view controllers are never deleted causing my app memory to keep growing memory consumption.
UITitleScreenViewController is my initial view controller. When I go from it to UIChooseAntViewController (a choose player screen) I want to relinquish ownership of UITitleViewController but as you can see in the instruments below the controller is still retained after the transition:
The second image is the retain/release history. All entries prior to #133 were issued on the app startup. I believe #133 and #140 are pairs created by the storyboard segue. So whose responsibility is to issue that extra release to destroy the controller? I tried to set self.view = nil on my willDidDisappear method but no deal.
Not only it is not releasing the controllers but it is creating new instances of them each time a transition. For instance, when I come back from ChooseAnt to Title it creates another instance of UITitleViewController!
Things that are important to say:
1) NSZombies flag is not ticked in the target scheme
2) There are no blocks in my UITitleViewController, and I commented out all blocks in UIChooseAntController. In fact these controllers are very simple. UITitle is entirely defined via storyboard (just a view with a background and two buttons performing segues)
while UIChooseAnt is a control that presents a background and a swipe interface to display available characters and radio buttons. The segue is performed programatically by calling [self performSegueWithIdentifier];
3) I don't know if this matters but the segues are defined as modal and have no animation.
EDIT: 4) None of the the controllers reference each other.
Below is the source code for the TitleViewController
This problem is driving me crazy. If anyone could shed some light on it. Anything would be of great help! Thanks!
#interface SMTitleScreenViewController ()
#property (weak, nonatomic) IBOutlet UIButton *buttonPlay;
#property (weak, nonatomic) IBOutlet UIButton *buttonCamera;
- (IBAction)onButtonPlay:(id)sender;
- (IBAction)onButtonCamera:(id)sender;
#end
#implementation SMTitleScreenViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor* color = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
UIFont* font = [UIFont fontWithName:#"Jungle Roar" size:BUTTON_FONT_SIZE];
NSString* playString = NSLocalizedString(#"Play", #"");
NSString* cameraString = NSLocalizedString(#"Camera", #"");
[self.buttonPlay setTitle:playString forState:UIControlStateNormal];
[self.buttonPlay setTitle:playString forState:UIControlStateHighlighted];
[self.buttonPlay setTitleColor:color forState:UIControlStateNormal];
[self.buttonPlay setTitleColor:color forState:UIControlStateHighlighted];
self.buttonPlay.titleLabel.font = font;
[self.buttonCamera setTitle:cameraString forState:UIControlStateNormal];
[self.buttonCamera setTitle:cameraString forState:UIControlStateHighlighted];
[self.buttonCamera setTitleColor:color forState:UIControlStateNormal];
[self.buttonCamera setTitleColor:color forState:UIControlStateHighlighted];
self.buttonCamera.titleLabel.font = font;
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (IBAction)onButtonPlay:(id)sender
{
}
- (IBAction)onButtonCamera:(id)sender
{
}
EDIT: UIChooseAntViewController (as requested)
#interface SMChooseAntViewController ()
#property (strong, nonatomic) UIImageView* rope;
#property (strong, nonatomic) UIImageView* antFrontLayer;
#property (strong, nonatomic) UIImageView* antBackLayer;
#property (strong, nonatomic) NSArray* antFrontImages;
#property (strong, nonatomic) NSArray* antBackImages;
#property (strong, nonatomic) NSArray* antNameImages;
#property (strong, nonatomic) UIButton* leftButton;
#property (strong, nonatomic) UIButton* rightButton;
#property (strong, nonatomic) UIButton* confirmButton;
#property (nonatomic) NSUInteger selectedAntID;
#property (strong, nonatomic) UIImage* radioImageHighlighted;
#property (strong, nonatomic) UIImage* radioImage;
#property (strong, nonatomic) NSMutableArray* radioViews;
#property (weak, nonatomic) IBOutlet UILabel *antDescriptionLabel;
#property (weak, nonatomic) IBOutlet UIImageView *antDescriptionBG;
#property (strong, nonatomic) UIImageView* antNameView;
#property (strong, nonatomic) UISwipeGestureRecognizer* leftSwipeRecognizer;
#property (strong, nonatomic) UISwipeGestureRecognizer* rightSwipeRecognizer;
- (void) onArrowButton:(id)sender;
- (void) onConfirmButton:(id)sender;
- (void) respondToSwipe:(UISwipeGestureRecognizer*)recognizer;
#end
#implementation SMChooseAntViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// Needed to come in between front and back player image layers
UIImage* ropeImage = [UIImage imageNamed:ROPE_IMAGE_PATH];
self.rope = [[UIImageView alloc] initWithImage:ropeImage];
self.rope.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height / 2.0f);
UIColor* brownColor = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
self.antDescriptionLabel.textColor = brownColor;
self.antDescriptionLabel.numberOfLines = 0;
NSArray* antNames = [SMProfile antNames];
// Cache available Player Views in a NSArray
UIImage* frontImages[MAX_AVAILABLE_ANTS];
UIImage* backImages[MAX_AVAILABLE_ANTS];
UIImage* nameImages[MAX_AVAILABLE_ANTS];
for (NSUInteger i = 0; i < MAX_AVAILABLE_ANTS; ++i)
{
NSString* antName = [antNames objectAtIndex:i];
frontImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_front.png", antName]];
backImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_back.png", antName]];
nameImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_name.png", antName]];
}
self.antFrontImages = [NSArray arrayWithObjects:frontImages[0], frontImages[1], frontImages[2], nil];
self.antBackImages = [NSArray arrayWithObjects:backImages[0], backImages[1], backImages[2], nil];
self.antNameImages = [NSArray arrayWithObjects:nameImages[0], nameImages[1], nameImages[2], nil];
// Load Selected player from profile
SMProfile* profile = [SMProfile mainProfile];
self.selectedAntID = profile.antID.unsignedIntegerValue;
self.antFrontLayer = [[UIImageView alloc] initWithImage:[self.antFrontImages objectAtIndex:self.selectedAntID]];
self.antBackLayer = [[UIImageView alloc] initWithImage:[self.antBackImages objectAtIndex:self.selectedAntID]];
self.antNameView = [[UIImageView alloc] initWithImage:[self.antNameImages objectAtIndex:self.selectedAntID]];
self.antNameView.center = CGPointMake(screenSize.width / 2.0f, self.antDescriptionBG.frame.origin.y);
NSString* antDescriptionKey = [NSString stringWithFormat:#"AntDescription%lu", (unsigned long)self.selectedAntID];
self.antDescriptionLabel.text = NSLocalizedString(antDescriptionKey, #"");
self.antDescriptionLabel.numberOfLines = 0;
self.antDescriptionLabel.adjustsFontSizeToFitWidth = YES;
self.antFrontLayer.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height * 0.75f);
self.antBackLayer.center = self.antFrontLayer.center;
// Here a perform button creation, loading and positioning
// No blocks are being called
// add Target to buttons
[self.leftButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.rightButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.confirmButton addTarget:self action:#selector(onConfirmButton:) forControlEvents:UIControlEventTouchUpInside];
// Create and configure SwipeRecognizers
self.leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:self.leftSwipeRecognizer];
self.rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:self.rightSwipeRecognizer];
// Here a create a custom page control scheme. I load two radio button images
// create views and add them to the root view node.
// Add remaining view to the hierarchy
[self.view addSubview:self.antBackLayer];
[self.view addSubview:self.rope];
[self.view addSubview:self.antFrontLayer];
[self.view addSubview:self.confirmButton];
[self.view bringSubviewToFront:self.antDescriptionBG];
[self.view bringSubviewToFront:self.antDescriptionLabel];
[self.view addSubview:self.leftButton];
[self.view addSubview:self.rightButton];
[self.view addSubview:self.antNameView];
[self.view bringSubviewToFront:[self.radioViews objectAtIndex:0]];
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.rope = nil;
self.antFrontLayer = nil;
self.antBackLayer = nil;
self.antFrontImages = nil;
self.antBackImages = nil;
self.antNameImages = nil;
self.leftButton = nil;
self.rightButton = nil;
self.confirmButton = nil;
self.radioImageHighlighted = nil;
self.radioImage = nil;
self.radioViews = nil;
self.antNameView = nil;
self.leftSwipeRecognizer = nil;
self.rightSwipeRecognizer = nil;
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)onArrowButton:(id)sender
{
UIButton* button = (UIButton*)sender;
NSInteger direction = button.tag;
// if on boundaries do nothing (first ant selected and swipe left or last ant selected and swipe right)
if ((self.selectedAntID == 0 && direction == -1) || (self.selectedAntID == (MAX_AVAILABLE_ANTS - 1) && direction == 1))
{
return;
}
// Update Radio Buttons. Unselect previous and select next.
UIImageView* currRadio = [self.radioViews objectAtIndex:self.selectedAntID];
currRadio.image = self.radioImage;
self.selectedAntID = (self.selectedAntID + MAX_AVAILABLE_ANTS + direction) % MAX_AVAILABLE_ANTS;
UIImageView* nextRadio = [self.radioViews objectAtIndex:self.selectedAntID];
nextRadio.image = self.radioImageHighlighted;
self.antFrontLayer.image = [self.antFrontImages objectAtIndex:self.selectedAntID];
self.antBackLayer.image = [self.antBackImages objectAtIndex:self.selectedAntID];
self.antNameView.image = [self.antNameImages objectAtIndex:self.selectedAntID];
// here I was issuing some block to perform the swipe animation for the ant image views. I commented them and I'm just replacing the images now (3 lines above)
}
- (void)onConfirmButton:(id)sender
{
// Save player choice to profile and perform segue
SMProfile* profile = [SMProfile mainProfile];
profile.antID = [NSNumber numberWithUnsignedInt:self.selectedAntID];
[profile save];
[self performSegueWithIdentifier:#"chooseAntToStageSelect" sender:self];
}
- (void) respondToSwipe:(UISwipeGestureRecognizer *)recognizer
{
// forward swipe to onArrowButton message
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft)
{
[self onArrowButton:self.rightButton];
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionRight)
{
[self onArrowButton:self.leftButton];
}
}
#end
When presenting B view controller from A, A will not release as A is the presentingViewController (please refer to the sdk doc).
Or if A,B are sub view controller of a navigation controller, A is store int he push stack which is not removed when pushing to B.
You are pushing a view controller on to a stack hence until the last one is not popped, the controller will not be released.
To go deep into dependencies on childs read the the article below.
Greatly explained, i am sure it'll help. :)
http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html