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.
Related
My xib of BetRecordAniChooserView :
My ViewController in simulator:
You can see the background view of the chooser-view height is reduce.
My code is below:
BetRecordAniChooserView.h:
#import <UIKit/UIKit.h>
typedef void(^ChooseBlock)(NSString *choosedStr);
#interface BetTRecordAniChooserView : UIView
#property (nonatomic, assign) UIViewController *owener;
#property (nonatomic, assign) BOOL isShow;
#property (nonatomic, copy) ChooseBlock block;
- (void)showSelf;
- (void)hideSelf;
#end
BetRecordAniChooserView.m:
#import "BetTRecordAniChooserView.h"
#interface BetTRecordAniChooserView ()
#property (weak, nonatomic) IBOutlet UIButton *all_button;
#property (weak, nonatomic) IBOutlet UIButton *check_pending_button;
#property (weak, nonatomic) IBOutlet UIButton *deposited_button;
#property (weak, nonatomic) IBOutlet UIButton *have_cancel_button;
#end
#implementation BetTRecordAniChooserView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (void)awakeFromNib {
[super awakeFromNib];
self.frame = CGRectMake(0, 0, self.bounds.size.width, 100);
self.all_button.selected = YES;
}
#pragma mark - actions
- (IBAction)allAction:(UIButton *)sender {
self.block(sender.titleLabel.text);
}
- (IBAction)checkPendingAction:(UIButton *)sender {
self.block(sender.titleLabel.text);
}
- (IBAction)haveDepositeAction:(UIButton *)sender {
self.block(sender.titleLabel.text);
}
- (IBAction)haveCancelAction:(UIButton *)sender {
self.block(sender.titleLabel.text);
}
#pragma mark - methods
- (void)showSelf {
CGRect temp_frame = self.frame;
self.isShow = YES;
[UIView animateWithDuration:0.3 animations:^{
self.frame = CGRectMake(temp_frame.origin.x, temp_frame.origin.y + temp_frame.size.height, temp_frame.size.width, temp_frame.size.height);
}];
}
- (void)hideSelf {
CGRect temp_frame = self.frame;
self.isShow = NO;
[UIView animateWithDuration:0.3 animations:^{
self.frame = CGRectMake(temp_frame.origin.x, temp_frame.origin.y - temp_frame.size.height, temp_frame.size.width, temp_frame.size.height);
} completion:^(BOOL finished) {
}];
}
#end
In my ViewController.m:
#import "ViewController.h"
#import "BetTRecordAniChooserView.h"
#interface ViewController ()
{
BetTRecordAniChooserView *_chooser_view;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_chooser_view = [[NSBundle mainBundle] loadNibNamed:#"BetTRecordAniChooserView" owner:self options:nil].firstObject;
//float width = self.view.bounds.size.width;
//float height = 100.f;
//_chooser_view.frame = CGRectMake(0, -height + 64, width, height);
_chooser_view.owener = self;
[self.view addSubview:_chooser_view];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)actionA:(UIButton *)sender {
if (_chooser_view.isShow) {
[_chooser_view hideSelf];
} else {
[_chooser_view showSelf];
}
}
#end
You can see in the BetRecordAniChooserView's awakeFromnNib method:
The frame height I set 100:
self.frame = CGRectMake(0, 0, self.bounds.size.width, 100);
But when I start my simulator it become 36(the gray color view under the buttons).
(lldb) po self.frame
(origin = (x = 0, y = 0), size = (width = 375, height = 36))
I found the reason:
At first I was use the trailing, leading, bottom, top of the gray back view to its superview, I get this issue.
And then I delete the Bottom Space Constraint, and add the height constraint to it.
Then I do not have the issue again, and I can drag out the height Constraint to the .m file too, convenience to change the height.
But I don't know if there is a method I do not use my set height constraint method, still use the trailing, leading, bottom, top constraints to get the requirement effect.
I am attempting to create a UI programmatically. My problem is the view controllers subviews are not showing.
Here is how I am trying to create the UI:
.h
#import <UIKit/UIKit.h>
#protocol TimerCreatorDelegate <NSObject>
- (void)timerCreatorControllerDismissedWithString:(NSString *)timerName andTimeInterval:(NSTimeInterval)timeInterval;
#end
#interface TimerCreatorController : UIViewController {
// id timerCreatorDelegate;
}
#property (nonatomic, assign) id<TimerCreatorDelegate> timerCreatorDelegate;
#property (weak, nonatomic) UIDatePicker *timerLength;
#property (weak, nonatomic) UITextField *timerNameTextField;
#property (weak, nonatomic) UIButton *createTimerButton;
//#property (weak, nonatomic) IBOutlet UIDatePicker *timerLength;
//#property (weak, nonatomic) IBOutlet UITextField *timerNameTextField;
- (IBAction)createTimer:(id)sender;
#end
.m
#import "TimerCreatorController.h"
#interface TimerCreatorController ()
#end
#implementation TimerCreatorController
#synthesize timerCreatorDelegate;
#synthesize timerNameTextField;
#synthesize timerLength;
#synthesize createTimerButton;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
timerNameTextField.placeholder = #"This timer is for:";
timerLength.datePickerMode = UIDatePickerModeCountDownTimer;
[createTimerButton setTitle:#"Start Timer" forState:UIControlStateNormal];
[createTimerButton setTitleColor:[UIColor colorWithRed:46/255 green:134/255 blue:53/255 alpha:1] forState:UIControlStateNormal];
createTimerButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (void)viewWillLayoutSubviews {
printf("viewWillLayoutSubviews Called.\n\n");
[self.view addSubview:timerLength];
[self.view addSubview:timerNameTextField];
[self.view addSubview:createTimerButton];
timerLength.translatesAutoresizingMaskIntoConstraints = false;
timerNameTextField.translatesAutoresizingMaskIntoConstraints = false;
createTimerButton.translatesAutoresizingMaskIntoConstraints = false;
timerLength.frame = CGRectMake(0, 0, self.view.frame.size.width, 216);
timerNameTextField.frame = CGRectMake((self.view.frame.size.width - 300) / 2, timerLength.frame.size.height + 40, 300, 30);
createTimerButton.frame = CGRectMake((self.view.frame.size.width - 96) / 2, timerNameTextField.frame.origin.y - 40, 96, 36);
[NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:
[timerLength.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[timerLength.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[timerLength.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
[timerLength.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[timerNameTextField.topAnchor constraintEqualToAnchor:timerLength.bottomAnchor],
[timerNameTextField.rightAnchor constraintEqualToAnchor:self.view.rightAnchor constant:-37],
[timerNameTextField.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:37],
// [timerNameTextField.heightAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:30],
[createTimerButton.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
// [createTimerButton.widthAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:96],
// [createTimerButton.heightAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:36],
[createTimerButton.topAnchor constraintEqualToAnchor:timerNameTextField.bottomAnchor]
#warning Finish Layout Constraints
, nil]];
NSString *timerLengthString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(timerLength.frame)];
NSString *timerNameTextFieldString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(timerNameTextField.frame)];
NSString *createTimerButtonString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(createTimerButton.frame)];
printf("timerLength: %s \n", [timerLengthString UTF8String]);
printf("timerNameTextFieldString: %s \n", [timerNameTextFieldString UTF8String]);
printf("createTimerButtonString: %s \n", [createTimerButtonString UTF8String]);
}
- (IBAction)createTimer:(id)sender {
if ([self.timerCreatorDelegate respondsToSelector:#selector(timerCreatorControllerDismissedWithString:andTimeInterval:)]) {
[self.timerCreatorDelegate timerCreatorControllerDismissedWithString:timerNameTextField.text andTimeInterval:timerLength.countDownDuration];
}
[self dismissViewControllerAnimated:true completion:nil];
}
#end
Despite me setting the frames of my views, the prints are showing frames of {{0,0},{0,0}}.
Where do you create the subviews? I see you trying to use them, but not where you are actually creating the objects you are using. Is it a .Nib file or are you actually doing everything programmatically? And why are your subviews weak references? If they are not held by anything else they will be released after they are created...
In Swift it will crash when you try to use an object before it is initialized. Objective-C is a little more forgiving and will just do nothing if you tell it to use a nil object (at least in the instances you are using).
I am using GitHub project https://github.com/mayuur/MJParallaxCollectionView
I am trying to add a UIView and UILabel to the cells being displayed. I have tried so many solutions it would probably just be easier to ask someone how to do it.
So with that can someone add a UIView and UILabel to the UICollectionView displaying some text? This can be done programmatically or via storyboard, whichever suits your style.
I tried adding related logic in MJCollectionViewCell.m setupImageView method. Also, tried MJRootViewController cellForItemAtIndexPath method. But I still can't get the UIView and UILabel to display over the UIImage object in MJCollectionViewCell.
#property (nonatomic, strong, readwrite) UIImage *image;
MJCollectionViewCell.h
//
// MJCollectionViewCell.h
// RCCPeakableImageSample
//
// Created by Mayur on 4/1/14.
// Copyright (c) 2014 RCCBox. All rights reserved.
//
#import <UIKit/UIKit.h>
#define IMAGE_HEIGHT 200
#define IMAGE_OFFSET_SPEED 25
#interface MJCollectionViewCell : UICollectionViewCell
/*
image used in the cell which will be having the parallax effect
*/
#property (nonatomic, strong, readwrite) UIImage *image;
/*
Image will always animate according to the imageOffset provided. Higher the value means higher offset for the image
*/
#property (nonatomic, assign, readwrite) CGPoint imageOffset;
//#property (nonatomic,readwrite) UILabel *textLabel;
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#property (weak, nonatomic) IBOutlet UIImageView *cellImage;
#property (nonatomic,readwrite) NSString *text;
#property(nonatomic,readwrite) CGFloat x,y,width,height;
#property (nonatomic,readwrite) NSInteger lineSpacing;
#property (nonatomic, strong) IBOutlet UIView* overlayView;
#end
MJCollectionViewCell.m
// MJCollectionViewCell.m
// RCCPeakableImageSample
//
// Created by Mayur on 4/1/14.
// Copyright (c) 2014 RCCBox. All rights reserved.
//
#import "MJCollectionViewCell.h"
#interface MJCollectionViewCell()
#property (nonatomic, strong, readwrite) UIImageView *MJImageView;
#end
#implementation MJCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) [self setupImageView];
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) [self setupImageView];
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#pragma mark - Setup Method
- (void)setupImageView
{
// Clip subviews
self.clipsToBounds = YES;
/*
// Add image subview
self.MJImageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, IMAGE_HEIGHT)];
self.MJImageView.backgroundColor = [UIColor redColor];
self.MJImageView.contentMode = UIViewContentModeScaleAspectFill;
self.MJImageView.clipsToBounds = NO;
[self addSubview:self.MJImageView];
*/
//New Code in method
// Add image subview
self.MJImageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, IMAGE_HEIGHT)];
self.MJImageView.backgroundColor = [UIColor redColor];
self.MJImageView.contentMode = UIViewContentModeScaleAspectFill;
self.MJImageView.clipsToBounds = NO;
//self.overlayView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
// UIView *anotherView = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, 20.0, 20.0)];
// UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0.0, 0.0, 50.0, 20.0)];
// label.text = #"Hello";
// [anotherView addSubview:label];
[self addSubview:self.MJImageView];
[self addSubview:self.overlayView];
[self addSubview:self.textLabel];
}
# pragma mark - Setters
- (void)setImage:(UIImage *)image
{
// Store image
self.MJImageView.image = image;
// Update padding
[self setImageOffset:self.imageOffset];
}
- (void)setImageOffset:(CGPoint)imageOffset
{
// Store padding value
_imageOffset = imageOffset;
// Grow image view
CGRect frame = self.MJImageView.bounds;
CGRect offsetFrame = CGRectOffset(frame, _imageOffset.x, _imageOffset.y);
self.MJImageView.frame = offsetFrame;
}
//This was added from MPSkewed may need to remove if not called.
- (void)setText:(NSString *)text{
_text=text;
if (!self.textLabel) {
CGFloat realH=self.height*2/3-self.lineSpacing;
CGFloat latoA=realH/3;
// self.textLabel=[[UILabel alloc] initWithFrame:CGRectMake(10,latoA/2, self.width-20, realH)];
self.textLabel.layer.anchorPoint=CGPointMake(.5, .5);
self.textLabel.font=[UIFont fontWithName:#"HelveticaNeue-ultralight" size:38];
self.textLabel.numberOfLines=3;
self.textLabel.textColor=[UIColor whiteColor];
self.textLabel.shadowColor=[UIColor blackColor];
self.textLabel.shadowOffset=CGSizeMake(1, 1);
self.textLabel.transform=CGAffineTransformMakeRotation(-(asin(latoA/(sqrt(self.width*self.width+latoA*latoA)))));
[self addSubview:self.textLabel];
}
self.textLabel.text=text;
}
#end
In the .h of the cell:
#property (strong, nonatomic, readonly) UILabel *label
In the .m of the cell:
#property (strong, nonatomic, readwrite) UILabel *label
In the setupImageView method (which you should probably rename to be more generic), after the image view is created, something like this:
self.label = [[UILabel alloc] initWithFrame:someFrame]; // or CGRectZero if you are using auto layout
self.label.textColor = ...; // any other setup you want to do
[self addSubview:self.label];
self.label.frame = ...; // or set it up using auto layout
And in your data source’s -collectionView:cellForItemAtIndexPath:
cell.label.text = #"some text";
That is a simple approach. In practice, I would probably expose a property for the string only, not the whole label, but that depends on what you are doing and how much control you need.
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];
}
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