UISlider not updating - ios

I am trying to code a music player for an assignment and I am stuck with trying to get the slider to update as the song is playing. I have been able to make it so that if the user were to slide the slider the song will rewind/fast-forward to that part of the song. Does anyone know why the slider doesn't continuously update as the song is playing?
Here is the .h file
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface AS3MPViewController : UIViewController <MPMediaPickerControllerDelegate>{
IBOutlet UIButton *playButton;
IBOutlet UIButton *stopButton;
IBOutlet UIButton *pauseButton;
IBOutlet UISlider *volumeSlider;
AVAudioPlayer *musicPlayer;
IBOutlet UIButton *pickSongButton;
IBOutlet UISlider *timeSlider;
NSTimer *timer;
}
#property (nonatomic, retain) UIButton *playButton;
#property (nonatomic, retain) UIButton *stopButton;
#property (nonatomic, retain) UIButton *pauseButton;
#property (nonatomic, retain) UISlider *volumeSlider;
#property (nonatomic, retain) AVAudioPlayer *musicPlayer;
#property (nonatomic, retain) UIButton *pickSongButton;
#property (nonatomic, retain) UISlider *timeSlider;
#property (nonatomic, retain) NSTimer *timer;
- (IBAction) play;
- (IBAction) stop;
- (IBAction) pause;
- (IBAction) changeVolume: (UISlider *)sender;
- (IBAction) chooseSong: (UIButton *)sender;
- (IBAction) changeTime: (UISlider *)sender;
- (void)setTimeSliderOptions;
#end
Here is the .m file
#import "AS3MPViewController.h"
#interface AS3MPViewController ()
#end
#implementation AS3MPViewController
#synthesize playButton = _playButton;
#synthesize stopButton = _stopButton;
#synthesize pauseButton = _pauseButton;
#synthesize musicPlayer = _musicPlayer;
#synthesize volumeSlider = _volumeSlider;
#synthesize pickSongButton = _pickSongButton;
#synthesize timeSlider = _timeSlider;
#synthesize timer;
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"animals001" ofType:#"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:filePath];
self.musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[self.musicPlayer prepareToPlay];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) play {
[self.musicPlayer play];
}
- (IBAction) stop {
[self.musicPlayer stop];
self.musicPlayer.currentTime = 0;
}
- (IBAction) pause {
[self.musicPlayer pause];
}
- (IBAction) changeVolume: (UISlider *)sender {
self.musicPlayer.volume = [sender value];
}
- (IBAction)chooseSong:(UIButton *)sender {
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio];
[mediaPicker setDelegate:self];
[mediaPicker setAllowsPickingMultipleItems:NO];
mediaPicker.prompt = NSLocalizedString(#"Add Some Songs To Play", "Prompt In Media Item Picker");
[self presentModalViewController: mediaPicker animated:YES];
}
- (void) mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
[self dismissModalViewControllerAnimated:YES];
}
- (void) mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems: (MPMediaItemCollection *)mediaItemCollection {
MPMediaItem *item = [[mediaItemCollection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
//AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
//AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
self.timeSlider.maximumValue = [self.musicPlayer duration];
self.musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[self.musicPlayer play];
timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(songCurrentTime) userInfo:nil repeats:YES];
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)changeTime: (UISlider *)sender {
self.musicPlayer.currentTime = self.musicPlayer.duration * sender.value;
}
- (void)songCurrentTime {
timeSlider.value = (self.musicPlayer.currentTime / self.musicPlayer.duration);
}
#end

check in the connections that you are connected the IBAction method with slider with ValueChanged
EX:
- (IBAction)changeTime: (UISlider *)sender {
check whether this method is connected with ValueChanged while IB connection

I think your problem is that you are updating the value of the slider from a thread other than the main one. Try modifying the songCurrentTime method so that it calls another one on the main thread that updates the value of the slider.
- (void)songCurrentTime {
[self performSelectorOnMainThread:#selector(songCurrentTimeMain) withObject:nil waitUntilDone:YES];
}
- (void)songCurrentTimeMain {
timeSlider.value = (self.musicPlayer.currentTime / self.musicPlayer.duration);
}
Of course to get correct results make sure the minimum value of the slider is 0 and the maximum is 1.
Best Regards

I've done something similar within this tutorial and the way I added a slider was the same as you did except for the fact that I used
musicPlayer.currentPlaybackTime
as a value to assign to the slider.
It may be that the result of (self.musicPlayer.currentTime / self.musicPlayer.duration) is an inaccurate assignment to your slider value.

When you set your slider value, the value is always less than or equal to 1 because self.musicPlayer.currentTime is always less than or equal to self.musicPlayer.duration. Instead, try this
timeSlider.value = self.musicPlayer.currentTime;

timeSlider and self.timeSlider are not the same thing. Most of the iVars that you have declared are unnecessary (and misleading) because you synthesize your properties using the underscore form of the property name. For example, you have a timeSlider property that's backed by a _timeSlider iVar, and a timeSlider iVar that's a different thing entirely.
Use self.timeSlider.value = ....

For me the issue was different i need to set initial value to slider, but it was a silly mistake that i am updating slider value before setting maximum and minimum value.
Wrong code :
float value = [self getSliderDefaultValue];
[slider setValue:value animated:NO];
slider.maximumValue = numberOfSteps;
slider.minimumValue = 0;
Working code :
slider.maximumValue = numberOfSteps;
slider.minimumValue = 0;
float value = [self getSliderDefaultValue];
[slider setValue:value animated:NO];
Hope it will help others!!!

There was transparent view over my UISlider in my case and i was overlooking in to it.

Related

I have an issue with an UIAlertView showing repeatedly can't find the source

My problem is this, in the app when a user clicks somewhere not important an alertView is raised that's ok, I can find the call to that view, but then is showing again and again empty and I have placed breakpoint everywhere I see a call to any alert. But the ghost alert is not breaking anywhere I have no idea who is throwing it is just a sentient view.
Can you give some tips on how to pin point where is the view being called?
EDIT:
Code for the viewController:
#import <CoreLocation/CoreLocation.h>
#import "FormViewController.h"
#import "FormPageViewController.h"
#import "FormElement+UtilityMethods.h"
#import "UserBO.h"
#import "RecordBO.h"
#import "RecordAnswer.h"
#import "UserDefaultsUtilities.h"
#import "TimeTrackingUtilities.h"
#import "DxColors.h"
#import "EDQueueUtilities.h"
#import "GroupAnswerMetadata.h"
#import "RecordAnswer+UtilityMethods.h"
#import "Record+UtilityMethods.h"
#import "FormPageIndexViewController.h"
#import "ManagedObjectUtilities.h"
#import "EDQueue.h"
#import "EDQueueUtilities.h"
#import "DxAnswerObject.h"
#import "ImageAnswerMetadata.h"
#import "DateUtilities.h"
#import <ifaddrs.h>
#import "CarbonKit.h"
#define INITIAL_CONTROLLER_INDEX 0
#define FORM_RECORDS_TEMP_NAME #"<~TMP>"
#define TAG_RETURN_BUTTON 0
#define TAG_SAVE_BUTTON 1
#define TAG_SEND_BUTTON 2
typedef NS_ENUM(NSUInteger, AlertViewPurpose) {
ALERT_VIEW_FORM_NONE = 0,
ALERT_VIEW_FORM_SEND_SUCCESS = 1,
ALERT_VIEW_FORM_SEND_FAILURE = 2,
ALERT_VIEW_FORM_SAVE_PROMPT = 3,
ALERT_VIEW_FORM_FILE_NAME_PROMPT = 4,
ALERT_VIEW_FORM_ASYNC_SEND_SUCCESS = 5,
ALERT_VIEW_FORM_COULDNT_SEND = 6,
ALERT_VIEW_FORM_WANT_TO_SEND = 7,
ALERT_VIEW_FORM_SAVE_IN_CONTEXT_PROMPT = 8,
ALERT_VIEW_FORM_FILE_NAME_IN_CTXT_SAVE_PROMPT = 9,
ALERT_VIEW_FORM_REQUIRED_INTERNET_CONECTION = 10,
// Enumeration counter.
ALERT_VIEW_PURPOSE_COUNT
};
// Based on:
// Ref.: http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/
#interface FormViewController () <RecordBOProtocol, FieldElementProtocol,
CLLocationManagerDelegate, FormPageIndexProtocol,CarbonTabSwipeNavigationDelegate>
{
AlertViewPurpose _currentAlertViewPurpose;
CarbonTabSwipeNavigation *_carbonTabSwipeNavigation;
BOOL _unedited;
BOOL _formRecordNilAtStartUp;
BOOL _timestampTaken;
CLLocationManager *_locationManager;
CLLocation *_location;
NSDate *_timeSpentBaseTimestamp;
NSArray *_sortedPages;
NSUInteger _currentPageIndex;
NSString *formID;
NSArray *_pagesNames;
}
#property (weak, nonatomic) IBOutlet UILabel *lblFormTitle;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallReturn;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallSave;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallSend;
#property (weak, nonatomic) IBOutlet UIButton *btnBigSend;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *btnReturn;
#property (strong, nonatomic) IBOutlet UIButton *lblBack;
#property (strong, nonatomic) IBOutlet UIButton *lblSave;
#end
#implementation FormViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_currentAlertViewPurpose = ALERT_VIEW_FORM_NONE;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self localizedButtons];
// Starting up location manager if form requires it.
// Ref.:
// https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html#//apple_ref/occ/instm/CLLocationManager/requestAlwaysAuthorization
if ([self.form.geolocationEnabled boolValue]) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
if ([CLLocationManager locationServicesEnabled]) {
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusNotDetermined) {
// Requesting authorization.
if ([CLLocationManager instancesRespondToSelector:#selector(requestWhenInUseAuthorization)]) {
#ifdef DEBUG_MODE
NSAssert(
[[[NSBundle mainBundle] infoDictionary] valueForKey:#"NSLocationWhenInUseUsageDescription"],
#"For iOS 8 and above, your app must have a value for NSLocationWhenInUseUsageDescription in its Info.plist");
#endif // DEBUG_MODE
[_locationManager requestWhenInUseAuthorization];
}
} else if (status == kCLAuthorizationStatusAuthorizedAlways ||
status == kCLAuthorizationStatusAuthorizedWhenInUse) {
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[_locationManager startUpdatingLocation];
}
}
}
self.lblFormTitle.text = self.form.name ;
// Saving whether self.formRecord was nil at beginning.
// Important for time spent tap calculations.
_formRecordNilAtStartUp = self.formRecord == nil;
[self setup];
//Take the time for counting
_timeSpentBaseTimestamp = [NSDate date];
_unedited = YES;
}
-(void)localizedButtons
{
[self.lblBack setTitle:NSLocalizedString(#"Back", #"Regresar") forState:UIControlStateNormal];
[self.lblSave setTitle:NSLocalizedString(#"Save", #"Guardar") forState:UIControlStateNormal];
[self.btnBigSend setTitle:NSLocalizedString(#"Send", #"Enviar") forState:UIControlStateNormal];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// Overriding from DxBaseViewController.
-(void)refresh
{
}
-(void)setup
{
// Obtaining sorted pages array.
_sortedPages = [[self.form.pages allObjects]
sortedArrayUsingComparator:^NSComparisonResult(Page *obj1, Page * obj2) {
return [obj1.pageNumber compare: obj2.pageNumber];
}];
//Adding toolBar
NSMutableArray *namesPages = [[NSMutableArray alloc]init];
for (Page *page in _sortedPages) {
NSString *namePage = page.name;
[namesPages addObject:namePage];
}
_pagesNames = [namesPages copy] ;
// Creating by default a record in case there's none.
if (self.formRecord == nil) {
self.formRecord = [Record createInContext:self.managedObjectContext];
// Filling in basic record information.
self.formRecord.name = FORM_RECORDS_TEMP_NAME;
self.formRecord.editable = self.form.editableRecords;
self.formRecord.dateLastSaved = self.formRecord.dateCreated = [NSDate date];
self.formRecord.syncStatusId = [NSNumber numberWithInt:SYNC_STATUS_NOT_SYNCED];
self.formRecord.user = [UserBO loggedInUser];
self.formRecord.form = self.form;
self.formRecord.formId = self.form.pkey;
self.formRecord.temporary = [NSNumber numberWithBool:YES];
self.formRecord.isBeingEdited = [NSNumber numberWithBool:YES];
// Committing record information as is. It will be removed if user doesn't
// want to save changes.
if (![Record commitChangesFromContext:self.managedObjectContext]) {
DebugLog(#"Temp form record couldn't be saved! Check!");
}
// Initializing page view controller.
_carbonTabSwipeNavigation =[[CarbonTabSwipeNavigation alloc] initWithItems:_pagesNames
delegate:self];
_carbonTabSwipeNavigation.toolbar.barTintColor = [DxColors colorWithHexRGB:NEW_FORMS_GREEN];
[_carbonTabSwipeNavigation setNormalColor:[UIColor whiteColor]];
[_carbonTabSwipeNavigation setIndicatorColor:[UIColor whiteColor]];
[_carbonTabSwipeNavigation setSelectedColor:[UIColor whiteColor]];
} else {
[self prepareControllerForEdition];
}
[_carbonTabSwipeNavigation insertIntoRootViewController:self];
self.pageViewController = _carbonTabSwipeNavigation.pageViewController;
}
- (UIViewController *)carbonTabSwipeNavigation:(CarbonTabSwipeNavigation *)carbontTabSwipeNavigation
viewControllerAtIndex:(NSUInteger)index {
_currentPageIndex = index;
// Create a new view controller and pass suitable data.
FormPageViewController *formPageViewController = [[FormPageViewController alloc] init];
formPageViewController.pageIndex = index;
formPageViewController.formPage = _sortedPages[index];
formPageViewController.managedObjectContext = self.managedObjectContext;
formPageViewController.formRecord = self.formRecord;
formPageViewController.observer = self;
formPageViewController.view.frame = CGRectMake(0,
0,
self.view.frame.size.width,
self.view.frame.size.height);
return formPageViewController;
}
#pragma mark - Button Actions (IBActions)
-(IBAction)send:(id)sender
{
_timer = [NSTimer scheduledTimerWithTimeInterval:0.001
target:self
selector:#selector(isAlertViewShowing:)
userInfo:nil
repeats:YES];
[self setButtonWithTag:self.btnBigSend.tag toHighlight:NO];
// Disabling button to avoid double submissions.
self.btnBigSend.enabled = NO;
// Show alert.
[self showAreYouReadyToSubmitFormMsg];
}
... can't paste it all
For testing only:
Subclass UIAlertView i.e. #interface MyAlertView : UIAlertView
Then replace all instances of UIAlertView from MyAlertView
i.e. MyAlertView *someAlert = [[MyAlertView alloc] init.......];
Then override
-(void)show {
[super show];
//Your breakpoint here
OR
NSLog([NSThread callStackSymbols]);
}
Check your viewcontroller that has an uialertviewdelegate.
Log your alertview.delegate
Check your super class of a viewcontroller that it doesn't call uialertviewdelegate function.
If it is an UIAlertController, check viewwillappear, viewdidappear, viewwilldisappear (super class too) and find out they don't call [alertview show]
Why you take enum for alertview ? just make instance of UIAlertView where it require's to show. you can make one method in which you can pass two string parameters alertview massage and title and method shows alertview with this title and massage.
You can catch the content of your AlertView, if it has no content at all, don't present it!
To do this check the message you are passing to the method that presents the alertView.
However, I can't seem to find your method showAreYouReadyToSubmitFormMsg.

IOS 8 xCode 6 button appears disabled but is not

Environment: IOS 8.02 Xcode 6
I have a form on a StoryBoard. It has 2 labels a text box and 2 buttons.
btnValidateAndSave disables all of the other controls on the form and calls a web service.
btnCance goes grey as if it has been disabled however it is clickable.
Below is the actual code behind the form.
#import "vcDigitalKeyManager.h"
#import "serviceManager.h"
#import "eziEnums.h"
#import "IsSystemLockedMethod.h"
#import "eziResponse.h"
#interface vcDigitalKeyManager ()
- (IBAction)btnValidateAndSave:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *txtDigitalKey;
#property (strong, nonatomic) IBOutlet UILabel *lblErrorMessage;
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *lblValidateActivity;
#property (strong, nonatomic) IBOutlet UIButton *btnValidateAndSave;
#property (strong, nonatomic) IBOutlet UIButton *btnCancel;
#property (strong, nonatomic) NSNumber *isWorking;
- (IBAction)btnCancel:(id)sender;
#end
#implementation vcDigitalKeyManager
#synthesize txtDigitalKey = _txtDigitalKey;
#synthesize lblErrorMessage = _lblErrorMessage;
#synthesize btnCancel = _btnCancel;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.lblValidateActivity setHidden:true];
}
- (IBAction)btnValidateAndSave:(id)sender {
[self.txtDigitalKey resignFirstResponder];
[self.txtDigitalKey setEnabled:NO];
[self.txtDigitalKey setUserInteractionEnabled:NO];
[self.lblValidateActivity setHidden:NO];
[self.btnValidateAndSave setEnabled:NO];
[self.btnValidateAndSave setUserInteractionEnabled:NO];
[self.txtDigitalKey setEnabled:NO];
[self.txtDigitalKey setUserInteractionEnabled:NO];
self.btnCancel.enabled=NO;
self.btnCancel.userInteractionEnabled = NO;
[self.lblValidateActivity startAnimating];
_isWorking = [NSNumber numberWithInt:1];
NSString * requestBody = [serviceManager buildSoapMessage:IsSystemLocked :self.txtDigitalKey.text ];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(displayEziResponse:)
name:#"kIsSystemLockedResponseNotification"
object:nil];
[[[IsSystemLockedMethod alloc]init] callNonPciMethod:requestBody servicetype:NonPci];
}
- (void) displayEziResponse:(NSNotification *) notification{
NSDictionary *userInfo = notification.userInfo;
eziResponse *myResponse = [userInfo objectForKey:#"someKey"];
if([myResponse.ErrorNumber isEqual:#"0"])
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else
{
[self.lblErrorMessage setText:[NSString stringWithFormat:#"%#: %#",myResponse.ErrorNumber, myResponse.ErrorMessage]];
[self.lblErrorMessage setTextColor:[UIColor redColor]];
[self.lblErrorMessage setHidden:NO];
[self.lblValidateActivity stopAnimating];
[self.lblValidateActivity setHidden:YES];
[self.btnValidateAndSave setEnabled:YES];
[self.btnValidateAndSave setUserInteractionEnabled:YES];
[self.txtDigitalKey setEnabled:YES];
[self.txtDigitalKey setUserInteractionEnabled:YES];
[self.btnCancel setEnabled:YES];
[self.btnCancel setUserInteractionEnabled:YES];
}
}
- (IBAction)btnCancel:(id)sender {
//NSLog([NSString stringWithFormat:#"%d",NSStringFromBOOL(BOOL self.btnCancel.enabled)]);
if(_isWorking == [NSNumber numberWithInt:0])
{
[self dismissViewControllerAnimated:YES completion:nil];
}
//if(self.btnCancel.enabled)
}
#end
I have tested this in the simulator and on an iPhone 5S
Any Ideas how to actually disable the button?
Prefer also disabling user interaction on the button !
[self.btnCancel setUserInteractionEnabled:NO];
I found the issue and it was not actually the button that was the issue.
The Validate and Save button calls a function that does an async call to a web service.
I am using notifications to let the main thread know when the response has been received.
The notification was being sent back on the background thread. The affect of this was that the Cancel button was being enabled in real time but not the visual state.
I modified the code so the notification is sent back to the main thread and now everything updates on the main thread within 1 second.

Particle emitter works in one app, but not another

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];
}

UIViewController won't be deleted

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

How to set UIButton tap sound in whole app?

Is there any way to set specific audio file as tap sound in UIButtons across whole iOS app without writing code in individual view?
You don't want to add any code in subclass . so you can subclass UIApplication like :
#interface PlaySound : UIApplication
#end
#implementation PlaySound
- (BOOL)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event{
if ([sender isKindOfClass:[UIButton class]]) {
//here, play sound
}
return [super sendAction:action to:target from:sender forEvent:event];
}
#end
and register your own application in main.m like:
int main(int argc, char *argv[])
{
#autoreleasepool {
NSString *appClass = #"PlaySound";
int retVal = UIApplicationMain(argc, argv, appClass, NSStringFromClass([AppDelegate class]));
return retVal;
}
}
then whenever people touches an button control , your custom application will receive this message and play sound. you don't have to write any code in any individual view.
You can achieve this by writing code once in AppDelegate file
In AppDelegate.m write a class method, like given below
+(void)playAlarmSound
{
NSString *sound_file;
if ((sound_file = [[NSBundle mainBundle] pathForResource:#"Output" ofType:#"aif"])){
NSURL *url = [[NSURL alloc] initFileURLWithPath:sound_file];
if (audioPlayer)
{
[audioPlayer release];
audioPlayer= nil;
}
AVAudioPlayer *audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL]autorelease];
audioPlayer.delegate = self;
[url release];
audioPlayer.numberOfLoops=0;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
}
In AppDelegate.h
+(void)playAlarmSound;
Now in your class where you want to call the above method write the below line
[AppDelegate playAlarmSound];
Note:- Import AVFoundation framework
I would say, create an 'Observer' class that plays the sound for all buttons are connected to. check this example.
creating a category is the way to go. answered by tiguero
.h:
#import <UIKit/UIKit.h>
#class SCLSoundEffect;
typedef enum {
SCLCLICKSOUND = 0,
SCLOTHERSOUND,
}
SCLSoundCategory;
#interface UIButton (soundEffect)
#property (nonatomic, strong) SCLSoundEffect *buttonSoundEffect;
+ (id) buttonWithType:(UIButtonType)buttonType andSound: (SCLSoundCategory)soundCategory;
- (void) playSound;
#end
.m:
#import "UIButton+soundEffect.h"
#import <objc/runtime.h>
#import "SCLSoundEffect.h"
static char const * const kButtonSoundEffectKey = "buttonSoundEffect";
#implementation UIButton (soundEffect)
#dynamic buttonSoundEffect;
+ (id) buttonWithType:(UIButtonType)buttonType andSound:(SCLSoundCategory) soundCategory;
{
UIButton *newButton = [UIButton buttonWithType:buttonType];
NSString *stringToUse = nil;
switch (soundCategory) {
case SCLCLICKSOUND:
stringToUse = #"button_sound.wav";
break;
case SCLOTHERSOUND:
assert(0); // To be defined
default:
break;
}
[newButton setButtonSoundEffect: [[SCLSoundEffect alloc] initWithSoundNamed:stringToUse]];
[newButton addTarget:newButton action:#selector(playSound) forControlEvents:UIControlEventTouchDown];
return newButton;
}
- (void) playSound
{
[self.buttonSoundEffect play];
}
- (SCLSoundEffect *)buttonSoundEffect {
return objc_getAssociatedObject(self, kButtonSoundEffectKey);
}
- (void)setButtonSoundEffect:(SCLSoundEffect *)buttonSoundEffect{
objc_setAssociatedObject(self, kButtonSoundEffectKey, buttonSoundEffect, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void) dealloc
{
[self setButtonSoundEffect:nil];
}
Now each time I create a button that play some sound I just need to use the following method:
UIButton *mySoundButton = [UIButton buttonWithType:UIButtonTypeCustom andSound:SCLCLICKSOUND];

Resources