I just finished a tutorial on making my own keypad. So I took that knowledge and made a real simple App that adds two numbers with my own keypad. It is just two numbers. But when I tap on one textfield, the other textfield gets edited! I have enclosed my code, any help will be much appreciated. And thanks to #spacious for all previous help.
#import "KeyPad2ViewController.h"
#interface KeyPad2ViewController ()
#property (nonatomic) BOOL userIsEnteringANumber;
#end
#implementation KeyPad2ViewController
#synthesize numberOne;
#synthesize numberTwo;
#synthesize result;
#synthesize keyPad;
#synthesize userIsEnteringANumber;
#synthesize currentTextField;
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if ([numberOne isFirstResponder])
{
self.currentTextField = self.numberOne;
self.userIsEnteringANumber = NO;
}
else if ([numberTwo isFirstResponder])
{
self.currentTextField = self.numberTwo;
self.userIsEnteringANumber = NO;
}
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[numberOne setInputView:keyPad];
[numberTwo setInputView:keyPad];
numberOne.delegate = self;
numberTwo.delegate = self;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setNumberOne:nil];
[self setNumberTwo:nil];
[self setResult:nil];
[self setKeyPad:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (IBAction)buttonPressed:(UIButton *)sender
{
NSString *button = [sender currentTitle];
NSRange decimalpoint = [self.currentTextField.text rangeOfString:#"."];
if (self.userIsEnteringANumber)
{
if ([button isEqualToString:#"."] && decimalpoint.location != NSNotFound)
{
self.currentTextField.text = self.currentTextField.text;
}else
self.currentTextField.text = [self.currentTextField.text stringByAppendingString:button];
}else
{
self.currentTextField.text = button;
self.userIsEnteringANumber = YES;
}
}
- (IBAction)backspacePressed:(UIButton *)sender
{
if (self.userIsEnteringANumber)
{
int positionOfDigitBeforeLastOne = self.currentTextField.text.length -1;
if (self.currentTextField.text.length > 0)
{
self.currentTextField.text = [self.currentTextField.text substringToIndex:positionOfDigitBeforeLastOne];
}
if (self.currentTextField.text.length == 0)
self.currentTextField.text = #"";
}
}
- (IBAction)clearPressed:(id)sender
{
currentTextField.text = [NSString stringWithFormat:#""];
}
- (IBAction)enterPressed:(UIButton *)sender
{
[self hideKeyPad];
}
- (IBAction)signPressed:(UIButton *)sender
{
NSString *negativeNumber = #"-";
NSString *firstCharInDisplay = #"";
if (self.userIsEnteringANumber)
{
firstCharInDisplay = [self.currentTextField.text substringToIndex:1];
if ([firstCharInDisplay isEqualToString:#"-"])
{
self.currentTextField.text = [self.currentTextField.text substringFromIndex:1];
}else {
negativeNumber = [negativeNumber stringByAppendingString:self.currentTextField.text];
self.currentTextField.text = negativeNumber;
}
}
}
- (IBAction)add:(UIButton *)sender
{
float a = [[numberOne text] floatValue];
float b = [[numberTwo text] floatValue];
float c = a + b;
[result setText:[NSString stringWithFormat:#"%.2f", c]];
}
- (void)showKeyPad
{
[UIView beginAnimations:#"animateView" context:nil];
[UIView setAnimationDuration:0.3];
CGRect keypadview = CGRectMake(0, 270, 320, 190); // puts keyboard on screen
keyPad.frame = keypadview;
keyPad.alpha = 1.0;
[UIView commitAnimations];
}
- (void)hideKeyPad
{
[UIView beginAnimations:#"animateView" context:nil];
[UIView setAnimationDuration:0.3];
CGRect keypadview = CGRectMake(0, 640, 320, 190); // puts keyboard off screen
keyPad.frame = keypadview;
keyPad.alpha = 1.0;
[UIView commitAnimations];
}
#end
and my .h file is
#import <UIKit/UIKit.h>
#interface KeyPad2ViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *numberOne;
#property (weak, nonatomic) IBOutlet UITextField *numberTwo;
#property (weak, nonatomic) IBOutlet UILabel *result;
#property (strong, nonatomic) IBOutlet UIView *keyPad;
#property (weak, nonatomic) UITextField *currentTextField;
- (IBAction)buttonPressed:(UIButton *)sender;
- (IBAction)backspacePressed:(UIButton *)sender;
- (IBAction)clearPressed:(UIButton *)sender;
- (IBAction)enterPressed:(UIButton *)sender;
- (IBAction)octopusPressed:(UIButton *)sender;
- (IBAction)signPressed:(UIButton *)sender;
- (IBAction)add:(UIButton *)sender;
#end
Thanks to any and all help. This site is really the best!!
When your textFieldShouldBeginEditing: method is called, the first responder has not been set to the correct field yet. Replace the textFieldShouldBeginEditing: with textFieldDidBeginEditing: to fix this problem.
Related
Step 1:-
Create a class with sub Class UITextView and put the name of class KDPlaceHolderTextView
copy and paste the code in KDPlaceHolderTextView.h file
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface KDPlaceHolderTextView : UITextView
#property (nonatomic, retain) IBInspectable NSString *placeholder;
#property (nonatomic, retain) IBInspectable UIColor *placeholderColor;
/*!
This method is used to set the UITextView Notification and UitextField begin nitification to set the placeholder text
#param NSNotification to be a notification
#return void
*/
-(void)textChanged:(NSNotification*)notification;
#end
Step 2:-
Add the this code in KDPlaceHolderTextView.m file
#import "KDPlaceHolderTextView.h"
#interface KDPlaceHolderTextView ()
#property (nonatomic, retain) UILabel *placeHolderLabel;
#end
#implementation KDPlaceHolderTextView
CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Use Interface Builder User Defined Runtime Attributes to set
// placeholder and placeholderColor in Interface Builder.
if (!self.placeholder) {
[self setPlaceholder:#""];
}
if (!self.placeholderColor) {
[self setPlaceholderColor:[UIColor lightGrayColor]];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
/*!
This method is used to set the frame Placeholder lable
#param CGrect frame to be a frame
#return id
*/
- (id)initWithFrame:(CGRect)frame
{
if( (self = [super initWithFrame:frame]) )
{
[self setPlaceholder:#""];
[self setPlaceholderColor:[UI Color lightGrayColor]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
- (void)textChanged:(NSNotification *)notification
{
if([[self placeholder] length] == 0)
{
return;
}
[UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{
if([[self text] length] == 0)
{
[[self viewWithTag:999] setAlpha:1];
}
else
{
[[self viewWithTag:999] setAlpha:0];
}
}];
}
- (void)setText:(NSString *)text {
[super setText:text];
[self textChanged:nil];
}
/*!
This method is used to draw the rect in placeholder lable acording to amount of text
#param CGrect to be a rect
#return void
*/
- (void)drawRect:(CGRect)rect
{
if( [[self placeholder] length] > 0 )
{
if (_placeHolderLabel == nil )
{
_placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)];
_placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
_placeHolderLabel.numberOfLines = 0;
_placeHolderLabel.font = self.font;
_placeHolderLabel.backgroundColor = [UIColor clearColor];
_placeHolderLabel.textColor = self.placeholderColor;
_placeHolderLabel.alpha = 0;
_placeHolderLabel.tag = 999;
[self addSubview:_placeHolderLabel];
}
_placeHolderLabel.text = self.placeholder;
[_placeHolderLabel sizeToFit];
[self sendSubviewToBack:_placeHolderLabel];
}
if( [[self text] length] == 0 && [[self placeholder] length] > 0 )
{
[[self viewWithTag:999] setAlpha:1];
}
[super drawRect:rect];
}
#end
Step 3:-
Go to storyboard file and drag the UITexview in viewcotroller and assign the class KDPlaceHolderTextView
and see the attribute inspector set the PlaceHolder text or color
finally build and run the project
Check this in GitHub
You just have to change class name to (GCPlaceholderTextView) in UITextView interface.
It is not directly possible to add a Placeholder in textView But we can make use of UILabel to do the same.
Take a property of UILabel
#property (weak, nonatomic) UILabel *placeholederLabel;
Then in YourViewDidLoad.
placeholederLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 5.0,textView.frame.size.width - 30.0, 40.0)];
[placeholederLabel setText:#"Enter Your text here."];
[placeholederLabel setBackgroundColor:[UIColor clearColor]];
[placeholederLabel setTextColor:[UIColor yellowColor]];
[textView addSubview:placeholederLabel];
And then write textView delegate Methods
- (void)textViewDidEndEditing:(UITextView *)theTextView
{
if (![textView hasText]) {
placeholederLabel.hidden = NO;
}
}
- (void) textViewDidChange:(UITextView *)textView
{
if(![textView hasText]) {
placeholederLabel.hidden = NO;
}
else{
placeholederLabel.hidden = YES;
}
}
I have several UIProgressViews that read the voltage of an external device attached to the iOS device. The view scales it from 0-5 volts and I wanted to know if there was a way to retrieve the current progress/voltage of the UIProgressView and compare it to a threshold that is input by a user. Once this threshold is surpassed; a method I have written should be called.
The problem isn't necessarily the comparison part or calling the method. I am having trouble retrieving a live update of what the current value of the progressview is.
Thanks for your time.
EDIT FOR PART II:
So the problem is I did not code this and it is code pertaining to a certain product called the Nanospark board. This board is an input/output control board for factory systems that is run by various iOS devices.
I cannot quite seem to figure out what the previous coder did within this class:
//
// AnalogVC.m
// NanosparkController
//
#import "AnalogVC.h"
#import "AppDelegate.h"
#interface InputItem : NSObject
#property (weak,nonatomic) UISwitch *onSwitch;
#property (weak,nonatomic) UIProgressView *progressView;
#property (weak,nonatomic) UILabel *valueLabel;
#end
#implementation InputItem
AppDelegate *appDelegate;
+ (id)itemWithSwitch:(id)temp progress:(id)progress label:(id)label
{
InputItem *item = [[InputItem alloc] init];
item.onSwitch = temp;
item.progressView = progress;
item.valueLabel = label;
return item;
}
- (void)setDisconnected
{
self.onSwitch.on = NO;
self.onSwitch.enabled = NO;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
}
- (void)setOn
{
self.onSwitch.on = YES;
self.onSwitch.enabled = YES;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
[appDelegate watchPins:#"testing ON"];
}
- (void)setOff
{
self.onSwitch.on = NO;
self.onSwitch.enabled = YES;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
[appDelegate watchPins:#"testing OFF"];
}
- (void)setValue:(double)value
{
if (self.onSwitch.on)
{
self.valueLabel.text = [NSString stringWithFormat:#"%0.3f v",value];
self.progressView.progress = value/5.0;
if(value > 0.8){
[appDelegate watchPins:#"testing VALUE"];
}
}
}
#end
#interface AnalogVC ()
{
NSArray *_inputItems;
AppDelegate *appDelegate;
NSMutableArray *channel0Values;
UIColor *custom1;
UIColor *custom2;
UIColor *custom3;
UIColor *custom4;
}
#property (nonatomic) NCBoardManager *manager;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch0;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch1;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch2;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch3;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch4;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch5;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress0;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress1;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress2;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress3;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress4;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress5;
#property (weak,nonatomic) IBOutlet UILabel *inputValue0;
#property (weak,nonatomic) IBOutlet UILabel *inputValue1;
#property (weak,nonatomic) IBOutlet UILabel *inputValue2;
#property (weak,nonatomic) IBOutlet UILabel *inputValue3;
#property (weak,nonatomic) IBOutlet UILabel *inputValue4;
#property (weak,nonatomic) IBOutlet UILabel *inputValue5;
#property (weak,nonatomic) IBOutlet UISlider *outputSlider0;
#property (weak,nonatomic) IBOutlet UISlider *outputSlider1;
#property (weak,nonatomic) IBOutlet UIStepper *outputStepper0;
#property (weak,nonatomic) IBOutlet UIStepper *outputStepper1;
#property (weak,nonatomic) IBOutlet UILabel *outputValue0;
#property (weak,nonatomic) IBOutlet UILabel *outputValue1;
- (IBAction)inputChannelChanged:(UISwitch *)sender;
- (IBAction)outputSliderMoved:(UISlider *)sender;
- (IBAction)outputSliderStopped:(UISlider *)sender;
- (IBAction)outputStepperChanged:(UIStepper *)sender;
#end
#implementation AnalogVC{}
//////////////////////////////
#pragma mark View Lifecycle
//////////////////////////////
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Analog VC loaded");
_inputItems = #[[InputItem itemWithSwitch:_inputSwitch0 progress:_inputProgress0 label:_inputValue0],
[InputItem itemWithSwitch:_inputSwitch1 progress:_inputProgress1 label:_inputValue1],
[InputItem itemWithSwitch:_inputSwitch2 progress:_inputProgress2 label:_inputValue2],
[InputItem itemWithSwitch:_inputSwitch3 progress:_inputProgress3 label:_inputValue3],
[InputItem itemWithSwitch:_inputSwitch4 progress:_inputProgress4 label:_inputValue4],
[InputItem itemWithSwitch:_inputSwitch5 progress:_inputProgress5 label:_inputValue5]];
_manager = [NCBoardManager sharedBoardManager];
__unsafe_unretained AnalogVC *vc = self;
[_manager setAnalogInputHandling:dispatch_get_main_queue()
filter:^(NCAnalogInputs *inputs){ return YES; }
handler:^(NCAnalogInputs *inputs){ [vc setAnalogInputs:inputs]; }];
// Register for notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boardConnected:)
name:CONNECTED_NOTIFICATION
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boardDisconnected:)
name:DISCONNECTED_NOTIFICATION
object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateAnalogInputs];
[self updateAnalogOutputs];
custom1 = [UIColor whiteColor];
custom2 = [UIColor darkGrayColor];
custom3 = [UIColor blackColor];
custom4 = [UIColor colorWithRed:.97 green:.97 blue:.588 alpha:1.0];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[custom2 CGColor], (id)[custom1 CGColor], (id)[custom2 CGColor], nil];
gradient.startPoint = CGPointMake(0.5, 0);
gradient.endPoint = CGPointMake(0.5, 1.0);
gradient.locations = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.5], [NSNumber numberWithFloat:1.0], nil];
[self.view.layer insertSublayer:gradient atIndex:0];
[self.inputSwitch0 setOnTintColor:custom4];
[self.inputSwitch1 setOnTintColor:custom4];
[self.inputSwitch2 setOnTintColor:custom4];
[self.inputSwitch3 setOnTintColor:custom4];
[self.inputSwitch4 setOnTintColor:custom4];
[self.inputSwitch5 setOnTintColor:custom4];
[self.inputSwitch0 setTintColor:custom3];
[self.inputSwitch1 setTintColor:custom3];
[self.inputSwitch2 setTintColor:custom3];
[self.inputSwitch3 setTintColor:custom3];
[self.inputSwitch4 setTintColor:custom3];
[self.inputSwitch5 setTintColor:custom3];
self.inputProgress0.trackTintColor = custom3;
self.inputProgress1.trackTintColor = custom3;
self.inputProgress2.trackTintColor = custom3;
self.inputProgress3.trackTintColor = custom3;
self.inputProgress4.trackTintColor = custom3;
self.inputProgress5.trackTintColor = custom3;
self.inputProgress0.progressTintColor = custom4;
self.inputProgress1.progressTintColor = custom4;
self.inputProgress2.progressTintColor = custom4;
self.inputProgress3.progressTintColor = custom4;
self.inputProgress4.progressTintColor = custom4;
self.inputProgress5.progressTintColor = custom4;
self.outputSlider0.minimumTrackTintColor = custom4;
self.outputSlider1.minimumTrackTintColor = custom4;
self.outputSlider0.maximumTrackTintColor = custom3;
self.outputSlider1.maximumTrackTintColor = custom3;
self.outputSlider0.thumbTintColor = custom3;
self.outputSlider1.thumbTintColor = custom3;
if(_manager.isBoardConnected)
{
self.outputStepper0.tintColor = custom4;
self.outputStepper1.tintColor = custom4;
self.outputStepper0.enabled = TRUE;
self.outputStepper1.enabled = TRUE;
self.outputSlider0.enabled = TRUE;
self.outputSlider1.enabled = TRUE;
}
else
{
self.outputStepper0.tintColor = custom2;
self.outputStepper1.tintColor = custom2;
self.outputStepper0.enabled = FALSE;
self.outputStepper1.enabled = FALSE;
self.outputSlider0.enabled = FALSE;
self.outputSlider1.enabled = FALSE;
}
}
//////////////////////////////
#pragma mark Rotation Calls
//////////////////////////////
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return FALSE;
}
//////////////////////////
#pragma mark Board Calls
//////////////////////////
- (void)boardConnected:(NSNotification *)notification
{
[self updateAnalogInputs];
[self updateAnalogOutputs];
self.outputStepper0.enabled = TRUE;
self.outputStepper1.enabled = TRUE;
self.outputSlider0.enabled = TRUE;
self.outputSlider1.enabled = TRUE;
self.outputStepper0.tintColor = custom4;
self.outputStepper1.tintColor = custom4;
}
- (void)boardDisconnected:(NSNotification *)notification
{
[self updateAnalogInputs];
[self updateAnalogOutputs];
self.outputStepper0.enabled = FALSE;
self.outputStepper1.enabled = FALSE;
self.outputSlider0.enabled = FALSE;
self.outputSlider1.enabled = FALSE;
self.outputStepper0.tintColor = custom2;
self.outputStepper1.tintColor = custom2;
}
- (void)updateAnalogInputs
{
uint8_t channel = self.manager.analogInputChannels;
switch (self.manager.analogInputStatus)
{
case NCInputConnected:
// Check if channels we left on
if (channel) self.manager.analogInputChannels = 0;
[_inputItems makeObjectsPerformSelector:#selector(setOff)];
break;
case NCInputDisconnected:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
case NCInputLiveUpdating:
for (InputItem *item in _inputItems)
{
//if (channel & 1) [item setOn];
//else [item setOff];
channel >>= 1;
}
break;
case NCInputSampling:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
case NCInputTransfering:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
}
}
- (void)setAnalogInputs:(NCAnalogInputs *)inputs
{
int i = 0;
uint8_t channels = inputs.channels;
for (InputItem *item in _inputItems)
{
if (channels & 1)
{
[item setValue:[inputs valueForChannel:i]];
}
channels >>= 1;
i++;
}
}
- (void)updateAnalogOutputs
{
BOOL connected = [self.manager isBoardConnected];
self.outputSlider0.value = self.manager.analogOutput0;
self.outputSlider0.enabled = connected;
self.outputStepper0.value = self.outputSlider0.value * 1000;
self.outputStepper0.enabled = connected;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",self.outputSlider0.value];
self.outputSlider1.value = self.manager.analogOutput1;
self.outputSlider1.enabled = connected;
self.outputStepper1.value = self.outputSlider1.value * 1000;
self.outputStepper1.enabled = connected;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",self.outputSlider1.value];
}
///////////////////////////////
#pragma mark IBAction Methods
///////////////////////////////
- (IBAction)inputChannelChanged:(UISwitch *)sender
{
NSLog(#"TEST");
InputItem *item = [_inputItems objectAtIndex:sender.tag];
uint8_t channels = self.manager.analogInputChannels;
if (sender.on)
{
channels |= (1 << sender.tag);
[item setOn];
}
else
{
channels &= ~(1 << sender.tag);
[item setOff];
}
if (!self.manager.analogInputChannels) [self.manager startAnalogLiveUpdating];
else if(!channels) [self.manager stopAnalogLiveUpdating];
self.manager.analogInputChannels = channels;
}
- (IBAction)outputSliderMoved:(UISlider *)sender
{
if (!sender.tag)
{
self.manager.analogOutput0 = sender.value;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",sender.value];
}
else
{
self.manager.analogOutput1 = sender.value;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",sender.value];
}
}
- (IBAction)outputSliderStopped:(UISlider *)sender
{
if (!sender.tag)
{
self.manager.analogOutput0 = sender.value;
self.outputStepper0.value = round(sender.value * 1000.0);
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",self.outputStepper0.value/1000.0];
}
else
{
self.manager.analogOutput1 = sender.value;
self.outputStepper1.value = round(sender.value * 1000.0);
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",self.outputStepper1.value/1000.0];
}
}
- (IBAction)outputStepperChanged:(UIStepper *)sender
{
float value = sender.value/1000.0;
if (!sender.tag)
{
self.manager.analogOutput0 = value;
self.outputSlider0.value = value;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",value];
}
else
{
self.manager.analogOutput1 = sender.value/1000.0;
self.outputSlider1.value = value;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",value];
}
}
#end
The problem I am having is I cannot figure out how to take values to and from the UIProgressViews that are on the storyboard (which is, like you said, a trivial concept). However with this set up it is rather convoluted.
Second problem is that; I am not sure if there is a way I can debug this as the application only runs when the external device (the nanospark controller board) is connected to the iPod.
The last but final problem I am having is that I am assuming the IBAction InputChannelChanged is being called (cannot debug this regularly with a breakpoint as aforementioned because it requires the external device to run the application), but when I run the application it does everything it should and the buttons react correctly to what the original software developer had coded.
This means that if I add my texting method to the IBAction (by adding [appDelegate watchPins#"TEST"]) it does not send the text to the user, but the buttons do still do what they should have done in concordance to the previous developers' aspirations.... this implies that the IBAction method is indeed being called... but then why isn't my text going through? I know that [appDelegate watchPins:#"TEST"]; should work as I have used it within several of his other classes.
Here is a screenshot displaying the UI of the AnalogVC.m:
http://i.stack.imgur.com/NNpZk.png
Do not feel obligated to answer all of these questions it's just I felt it necessary to provided all three for greater context of the problem. Thanks and sorry for the TL;DR.
EDIT 2: I'd upload the image but I do not have the required minimum reputation.
EDIT 3: I have tried to add another IBAction just to see if the buttons react to that; still nothing even if I copied the exact code from the other working classes.
Solution 1:
You need to call your method when you update the UIProgressView I assume that use are using the setProgress: animated: method somewhere in your code to update the progress view. If so, try this code in the method in which you update the UIProgressView:
float myVoltageFromDevice = self.deviceInput / 5.0; //this can be whatever your input is mapped to 0.0...1.0
//the self.device input variable should be the input from the external device.
if(myFloatFromDevice > myThreshold){ //set my threshold to whatever you would like.
[self doAction]; //do whatever you need to do when the value is surpassed
}else{
[myProgressView setProgress:myVoltageFromDevice animated: NO];
}
Edit:
I commented in the code above //this can be whatever your input is mapped to 0.0...1.0. Just in case this isn't clear, to achieve mapping you would do:
float myVoltageFromDevice = self.deviceInput / 5.0;
because the device input variable should be a value from 0-5 as you said in the OP. This makes the value from 0.0-1.0 which is the range of values that UIProgressView will accept.
Solution 2:
If can't pull off the above (which you really should be able to do), you should use Key Value Observing (KVO) which is detailed in this apple developer doc.
Edit 2:
The code you posted is quite complicated, but I believe that the method that you need to edit is - (void)setAnalogInputs:(NCAnalogInputs *)inputs try changing some of the code to this:
for (InputItem *item in _inputItems)
{
if (channels & 1)
{
if([inputs valueForChannel:i] > myThreshold){
[self doAction]; //Do your action here.
}else{
[item setValue:[inputs valueForChannel:i]];
}
}
channels >>= 1;
i++;
}
The issue is that my UITextField wrapper class isn't getting the UITextField to appear within a UITableViewCell (however, a regular UITextField view works just fine):
Here is my custom UITextField:
Header:
#import <UIKit/UIKit.h>
#protocol TUTextFieldDelegate <NSObject>
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string;
- (BOOL)textFieldShouldClear:(UITextField *)textField;
- (void)textFieldDidBeginEditing:(UITextField *)textField;
- (void)textFieldDidEndEditing:(UITextField *)textField;
#end
#interface TUTextField : UIControl <UITextFieldDelegate>
#property (nonatomic, assign) BOOL fixedDecimalPoint;
#property (nonatomic, assign) id <TUTextFieldDelegate>delegate;
#property (nonatomic, assign) UIKeyboardType keyboardType;
#property (nonatomic, strong) NSString *placeholder;
#property (nonatomic, assign) UITextBorderStyle borderStyle;
#property (nonatomic, assign) BOOL adjustsFontSizeToFitWidth;
#property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
#property (nonatomic, strong) NSString *text;
- (id)initWithTextField:(UITextField *)textField;
- (id)initWithFrame:(CGRect)frame;
#end
Implementation:
#import "TUTextField.h"
#interface TUTextField ()
#property (nonatomic, strong) UITextField *textField;
#end
#implementation TUTextField
- (id)initWithTextField:(UITextField *)textField
{
self = [super initWithFrame:textField.frame];
if (self) {
self.textField = textField;
_textField.delegate = self;
_fixedDecimalPoint = NO;
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.textField = [[UITextField alloc] initWithFrame:frame];
_textField.delegate = self;
[self addSubview:_textField];
_fixedDecimalPoint = NO;
}
return self;
}
- (void)setKeyboardType:(UIKeyboardType)keyboardType {
_textField.keyboardType = keyboardType;
}
- (void)setPlaceholder:(NSString *)placeholder {
_textField.placeholder = placeholder;
}
- (void)setBorderStyle:(UITextBorderStyle)borderStyle {
_textField.borderStyle = borderStyle;
}
-(void)setAdjustsFontSizeToFitWidth:(BOOL)adjustsFontSizeToFitWidth {
_textField.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth;
}
-(void)setClearButtonMode:(UITextFieldViewMode)clearButtonMode {
_textField.clearButtonMode = clearButtonMode;
}
-(void)setText:(NSString *)text {
_textField.text = text;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[self addSubview:self.textField];
}
*/
- (void)viewDidLoad {
[self addSubview:_textField];
}
- (void)viewWillAppear {
[self addSubview:_textField];
}
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
if ([_delegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [_delegate textField:textField shouldChangeCharactersInRange:range
replacementString:string];
}
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
if ([_delegate respondsToSelector:#selector(textFieldShouldClear:)]) {
return [_delegate textFieldShouldClear:textField];
}
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if ([_delegate respondsToSelector:#selector(textFieldDidBeginEditing:)]) {
[_delegate textFieldDidBeginEditing:textField];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([_delegate respondsToSelector:#selector(textFieldDidEndEditing:)]) {
[_delegate textFieldDidEndEditing:textField];
}
}
#end
And here is the code to add my custom wrapper to the table view:
} else if (indexPath.row == 1) {
cell.textLabel.text = #"Tax Rate";
if (!_textField) {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(220.0, 8.0, 60.0, 26.0)];
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.adjustsFontSizeToFitWidth = YES;
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.keyboardType = UIKeyboardTypeDecimalPad;
textField.placeholder = kTaxRateDefault;
_textField = [[TUTextField alloc] initWithTextField:textField];
_textField.delegate = self;
// retrieve saved settings
_taxRate = [[SettingsSingleton sharedManager] taxRate];
if (![_taxRate isEqualToString:#""])
_textField.text = _taxRate;
}
UILabel *percentSign = [[UILabel alloc] initWithFrame:CGRectMake(285.0, 6.0, 20.0, 26.0)];
percentSign.text = #"%";
percentSign.backgroundColor = [UIColor clearColor];
[cell addSubview:_textField];
[cell addSubview:percentSign];
}
}
Any help appreciated!
I don't think this is the best way to extend a UITextField.
Instead of making an NSObject with a UITextField under it you should be saying something like:
Interface:
#interface UITextField (extendedUITextField) {
// Variables
}
//Methods
#end
Implementation:
#implementation UITextField (extendedUITextField)
// Method implement
#end
Hope this helps.
I was wondering if anyone has any idea how to connect a text field with a slider. I want the slider to list numbers from 1-100 or 1-1000. I've looked places but there is no true answer to what I'm looking for. Your help would be greatly appreciated
ViewController.h
#interface ViewController : UIViewController {
IBOutlet UISlider *mySlider;
IBOutlet UITextField *myTextField;
}
#property (nonatomic, retain) IBOutlet UISlider *mySlider;
#property (nonatomic, retain) IBOutlet UITextField *myTextField;
- (IBAction) sliderValueChanged:(id)sender;
- (IBAction) changeButtonPressed:(id)sender;
ViewController.m
#import "ViewController.h"
#implementation TutorialProjectViewController
#synthesize mySlider, myTextField;
- (void)viewDidLoad {
[super viewDidLoad];
self.myTextField.delegate = self;
}
- (IBAction) sliderValueChanged:(UISlider *)sender {
myTextField.text = [NSString stringWithFormat:#" %.1f", [sender value]];
}
- (IBAction) changeButtonPressed:(id)sender {
NSString *textValue = [myTextField text];
float value = [textValue floatValue];
if (value < 0) value = 0;
if (value > 100) value = 100;
mySlider.value = value;
myTextField.text = [NSString stringWithFormat:#"%.1f", value];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[myTextField resignFirstResponder];
//[put your textfield name here resignFirstResponder];
return NO;
}
#end
Change Slider Value:
I'm assuming you want to update the text field's text as the slider is changed.
You don't connect the two. You need to listen for changes to the slider's value. In the method that you setup for this, you need to convert the slider's value to the text you want. You then update the text field's text with this new text based on the slider's value.
The hook for the slider:
[someSlider addTarget:self action:#selector(sliderUpdate:) forControlEvents:UIControlEventValueChanged];
The method that listens to the slider:
- (void)sliderUpdate:(UISlider *)slider {
float newVal = slider.value;
NSString *newText = [NSString stringWithFormat:#"%d", (int)newVal];
someTextField.text = newText;
}
This code assumes the slider is referenced in the someSlider ivar and the text field is referenced by the someTextField ivar. It also assumes the slider's minimumValue is 0 and the maximumValue is 100 or 1000 as needed.
#import "FirstViewController.h"
#interface FirstViewController ()
#property (weak, nonatomic) IBOutlet UITextField *myTextBox;
#property (weak, nonatomic) IBOutlet UISlider *mySlider;
#property (weak, nonatomic) IBOutlet UINavigationBar *titleOnTop;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *backButton;
- (IBAction)changeGreeting:(id)sender;
- (IBAction)sliderValueChanged:(id)sender;
- (IBAction)changeButtonPressed:(id)sender;
#end
#implementation FirstViewController
#synthesize mySlider, myTextBox;
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTextBox.delegate = self;
}
- (IBAction) sliderValueChanged:(UISlider *)sender
{
myTextBox.text = [NSString stringWithFormat:#"%.1f", [sender value]];
}
- (void)sliderUpdate:(UISlider *)slider {
float newVal = slider.value;
NSString *newText = [NSString stringWithFormat:#"%d", (int)newVal];
myTextBox.text = newText;
}
- (IBAction) changeButtonPressed:(id)sender
{
NSString *textValue = [myTextBox text];
float value = [textValue floatValue];
if (value < 0) value = 0;
if (value > 1000) value = 1000;
mySlider.value = value;
myTextBox.text = [NSString stringWithFormat:#"%.1f", value];
if ([myTextBox canResignFirstResponder]) [myTextBox resignFirstResponder];
}
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
if (myTextBox)
{
if ([myTextBox canResignFirstResponder]) [myTextBox resignFirstResponder];
}
[super touchesBegan: touches withEvent: event];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (IBAction)changeGreeting:(id)sender {
self.username = self.myTextBox.text;
NSString *nameString = self.username;
if ([nameString length] == 0) {
nameString = #"World";
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[myTextBox resignFirstResponder];
return YES;
}
#end
#import "FirstViewController.h"
#interface FirstViewController ()
#property (weak, nonatomic) IBOutlet UITextField *myTextBox;
#property (weak, nonatomic) IBOutlet UISlider *mySlider;
#property (weak, nonatomic) IBOutlet UINavigationBar *titleOnTop;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *backButton;
- (IBAction)changeGreeting:(id)sender;
- (IBAction)sliderValueChanged:(id)sender;
- (IBAction)changeButtonPressed:(id)sender;
- (IBAction)backgroundTouched:(id)sender;
- (IBAction)textFieldReturn:(id)sender;
#end
#implementation FirstViewController
#synthesize mySlider, myTextBox;
- (IBAction)backgroundTouched:(id)sender;
{
[myTextBox resignFirstResponder];
}
- (IBAction)textFieldReturn:(id)sender;
{
[sender resignFirstResponder];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTextBox.delegate = self;
}
- (IBAction) sliderValueChanged:(UISlider *)sender
{
myTextBox.text = [NSString stringWithFormat:#"%.1f", [sender value]];
}
- (void)sliderUpdate:(UISlider *)slider {
float newVal = slider.value;
NSString *newText = [NSString stringWithFormat:#"%d", (int)newVal];
myTextBox.text = newText;
}
- (void)textFieldDidChange:(UITextField*)sender { slider.value = [sender.text floatValue]; }
- (IBAction) changeButtonPressed:(id)sender
{
NSString *textValue = [myTextBox text];
float value = [textValue floatValue];
if (value < 0) value = 0;
if (value > 1000) value = 1000;
mySlider.value = value;
myTextBox.text = [NSString stringWithFormat:#"%.1f", value];
if ([myTextBox canResignFirstResponder]) [myTextBox resignFirstResponder];
}
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
if (myTextBox)
{
if ([myTextBox canResignFirstResponder]) [myTextBox resignFirstResponder];
}
[super touchesBegan: touches withEvent: event];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
[self.textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (IBAction)changeGreeting:(id)sender {
self.username = self.myTextBox.text;
NSString *nameString = self.username;
if ([nameString length] == 0) {
nameString = #"World";
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[myTextBox resignFirstResponder];
return NO;
}
#end
I'm working on a soundboard app, that has several pages of buttons to play sound effects with a stop button on every page should the user wish to manually interrupt the clip. I'm using avaudioplayer in each view to play the sound upon pressing the button for that clip. It works fine until the view is changed. If a user jumps to a new page the sound keeps playing and the stop button stops working even if they return to the original view. Pressing a sound button no longer interrupts the running sound resulting in two sounds over each other.
From googling and searching this site, I know the issue is that each view change creates a new instance of the player and the remedy is to create a singleton class. Unfortunately I have yet to find any further examples of how to actually do this. If someone could provide or point the way to a beginners guide for creating an avaudioplayer singleton I would really appreciate it. All I need to be able to do is pass the file name to the shared player and start play with a sound clip button and have the stop button stop sounds no matter what view the user is on. I am using the ios 5.1 sdk with storyboards and ARC enabled.
My solution, as used in one of my own projects, is posted beneath. Feel free to copy-and-paste, I intend to open-source this project once it's finished :)
A preview of the player can be seen on YouTube: http://www.youtube.com/watch?v=Q98DQ6iNTYM
AudioPlayer.h
#protocol AudioPlayerDelegate;
#interface AudioPlayer : NSObject
#property (nonatomic, assign, readonly) BOOL isPlaying;
#property (nonatomic, assign) id <AudioPlayerDelegate> delegate;
+ (AudioPlayer *)sharedAudioPlayer;
- (void)playAudioAtURL:(NSURL *)URL;
- (void)play;
- (void)pause;
#end
#protocol AudioPlayerDelegate <NSObject>
#optional
- (void)audioPlayerDidStartPlaying;
- (void)audioPlayerDidStartBuffering;
- (void)audioPlayerDidPause;
- (void)audioPlayerDidFinishPlaying;
#end
AudioPlayer.m
// import AVPlayer.h & AVPlayerItem.h
#interface AudioPlayer ()
- (void)playerItemDidFinishPlaying:(id)sender;
#end
#implementation AudioPlayer
{
AVPlayer *player;
}
#synthesize isPlaying, delegate;
+ (AudioPlayer *)sharedAudioPlayer
{
static dispatch_once_t pred;
static AudioPlayer *sharedAudioPlayer = nil;
dispatch_once(&pred, ^
{
sharedAudioPlayer = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:sharedAudioPlayer selector:#selector(playerItemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
});
return sharedAudioPlayer;
}
- (void)playAudioAtURL:(NSURL *)URL
{
if (player)
{
[player removeObserver:self forKeyPath:#"status"];
[player pause];
}
player = [AVPlayer playerWithURL:URL];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
if (delegate && [delegate respondsToSelector:#selector(audioPlayerDidStartBuffering)])
[delegate audioPlayerDidStartBuffering];
}
- (void)play
{
if (player)
{
[player play];
if (delegate && [delegate respondsToSelector:#selector(audioPlayerDidStartPlaying)])
[delegate audioPlayerDidStartPlaying];
}
}
- (void)pause
{
if (player)
{
[player pause];
if (delegate && [delegate respondsToSelector:#selector(audioPlayerDidPause)])
[delegate audioPlayerDidPause];
}
}
- (BOOL)isPlaying
{
DLog(#"%f", player.rate);
return (player.rate > 0);
}
#pragma mark - AV player
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == player && [keyPath isEqualToString:#"status"])
{
if (player.status == AVPlayerStatusReadyToPlay)
{
[self play];
}
}
}
#pragma mark - Private methods
- (void)playerItemDidFinishPlaying:(id)sender
{
DLog(#"%#", sender);
if (delegate && [delegate respondsToSelector:#selector(audioPlayerDidFinishPlaying)])
[delegate audioPlayerDidFinishPlaying];
}
#end
AudioPlayerViewController.h
extern NSString *const kAudioPlayerWillShowNotification;
extern NSString *const kAudioPlayerWillHideNotification;
#interface AudioPlayerViewController : UIViewController
#property (nonatomic, assign, readonly) BOOL isPlaying;
#property (nonatomic, assign, readonly) BOOL isPlayerVisible;
- (void)playAudioAtURL:(NSURL *)URL withTitle:(NSString *)title;
- (void)pause;
#end
AudioPlayerViewController.m
NSString *const kAudioPlayerWillShowNotification = #"kAudioPlayerWillShowNotification";
NSString *const kAudioPlayerWillHideNotification = #"kAudioPlayerWillHideNotification";
#interface AudioPlayerViewController () <AudioPlayerDelegate>
#property (nonatomic, strong) AudioPlayerView *playerView;
- (void)playButtonTouched:(id)sender;
- (void)closeButtonTouched:(id)sender;
- (void)hidePlayer;
#end
#implementation AudioPlayerViewController
#synthesize playerView, isPlaying, isPlayerVisible;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
playerView = [[AudioPlayerView alloc] initWithFrame:CGRectZero];
[AudioPlayer sharedAudioPlayer].delegate = self;
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
self.view = playerView;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
[playerView.playButton addTarget:self action:#selector(playButtonTouched:) forControlEvents:UIControlEventTouchUpInside];
[playerView.closeButton addTarget:self action:#selector(closeButtonTouched:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Private methods
- (AudioPlayerView *)playerView
{
return (AudioPlayerView *)self.view;
}
- (void)hidePlayer
{
[[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillHideNotification object:nil];
[self.playerView hidePlayer];
}
- (void)playButtonTouched:(id)sender
{
DLog(#"play / pause");
if ([AudioPlayer sharedAudioPlayer].isPlaying)
{
[[AudioPlayer sharedAudioPlayer] pause];
}
else
{
[[AudioPlayer sharedAudioPlayer] play];
}
[self.playerView showPlayer];
}
- (void)closeButtonTouched:(id)sender
{
DLog(#"close");
if ([AudioPlayer sharedAudioPlayer].isPlaying)
[[AudioPlayer sharedAudioPlayer] pause];
[self hidePlayer];
}
#pragma mark - Instance methods
- (void)playAudioAtURL:(NSURL *)URL withTitle:(NSString *)title
{
playerView.titleLabel.text = title;
[[AudioPlayer sharedAudioPlayer] playAudioAtURL:URL];
[[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillShowNotification object:nil];
[playerView showPlayer];
}
- (void)pause
{
[[AudioPlayer sharedAudioPlayer] pause];
[[NSNotificationCenter defaultCenter] postNotificationName:kAudioPlayerWillHideNotification object:nil];
[playerView hidePlayer];
}
#pragma mark - Audio player delegate
- (void)audioPlayerDidStartPlaying
{
DLog(#"did start playing");
playerView.playButtonStyle = PlayButtonStylePause;
}
- (void)audioPlayerDidStartBuffering
{
DLog(#"did start buffering");
playerView.playButtonStyle = PlayButtonStyleActivity;
}
- (void)audioPlayerDidPause
{
DLog(#"did pause");
playerView.playButtonStyle = PlayButtonStylePlay;
}
- (void)audioPlayerDidFinishPlaying
{
[self hidePlayer];
}
#pragma mark - Properties
- (BOOL)isPlaying
{
return [AudioPlayer sharedAudioPlayer].isPlaying;
}
- (BOOL)isPlayerVisible
{
return !playerView.isPlayerHidden;
}
#end
AudioPlayerView.h
typedef enum
{
PlayButtonStylePlay = 0,
PlayButtonStylePause,
PlayButtonStyleActivity,
} PlayButtonStyle;
#interface AudioPlayerView : UIView
#property (nonatomic, strong) UIButton *playButton;
#property (nonatomic, strong) UIButton *closeButton;
#property (nonatomic, strong) UILabel *titleLabel;
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
#property (nonatomic, assign) PlayButtonStyle playButtonStyle;
#property (nonatomic, assign, readonly) BOOL isPlayerHidden;
- (void)showPlayer;
- (void)hidePlayer;
#end
AudioPlayerView.m
#implementation AudioPlayerView
{
BOOL _isAnimating;
}
#synthesize playButton, closeButton, titleLabel, playButtonStyle, activityView, isPlayerHidden = _playerHidden;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"musicplayer_background.png"]];
_playerHidden = YES;
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityView.frame = CGRectMake(0.0f, 0.0f, 30.0f, 30.0f);
[self addSubview:activityView];
playButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];
[playButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[playButton setBackgroundImage:[UIImage imageNamed:#"button_pause.png"] forState:UIControlStateNormal];
playButton.titleLabel.textAlignment = UITextAlignmentCenter;
[self addSubview:playButton];
closeButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];
[closeButton setBackgroundImage:[UIImage imageNamed:#"button_close.png"] forState:UIControlStateNormal];
[closeButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
closeButton.titleLabel.textAlignment = UITextAlignmentCenter;
[self addSubview:closeButton];
titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 240.0f, 30.0f)];
titleLabel.text = nil;
titleLabel.textAlignment = UITextAlignmentCenter;
titleLabel.font = [UIFont boldSystemFontOfSize:13.0f];
titleLabel.numberOfLines = 2;
titleLabel.textColor = [UIColor whiteColor];
titleLabel.backgroundColor = [UIColor clearColor];
[self addSubview:titleLabel];
}
return self;
}
- (void)layoutSubviews
{
#define PADDING 5.0f
DLog(#"%#", NSStringFromCGRect(self.bounds));
CGRect frame = self.bounds;
CGFloat y = frame.size.height / 2;
titleLabel.center = CGPointMake(frame.size.width / 2, y);
CGFloat x = titleLabel.frame.origin.x - (playButton.frame.size.width / 2) - PADDING;
playButton.center = CGPointMake(x, y);
activityView.center = CGPointMake(x, y);
x = titleLabel.frame.origin.x + titleLabel.frame.size.width + (closeButton.frame.size.width / 2) + PADDING;
closeButton.center = CGPointMake(x, y);
}
#pragma mark - Instance methods
- (void)showPlayer
{
if (_isAnimating || _playerHidden == NO)
return;
_isAnimating = YES;
[UIView
animateWithDuration:0.5f
animations:^
{
CGRect frame = self.frame;
frame.origin.y -= 40.0f;
self.frame = frame;
}
completion:^ (BOOL finished)
{
_isAnimating = NO;
_playerHidden = NO;
}];
}
- (void)hidePlayer
{
if (_isAnimating || _playerHidden)
return;
_isAnimating = YES;
[UIView
animateWithDuration:0.5f
animations:^
{
CGRect frame = self.frame;
frame.origin.y += 40.0f;
self.frame = frame;
}
completion:^ (BOOL finished)
{
_isAnimating = NO;
_playerHidden = YES;
}];
}
- (void)setPlayButtonStyle:(PlayButtonStyle)style
{
playButton.hidden = (style == PlayButtonStyleActivity);
activityView.hidden = (style != PlayButtonStyleActivity);
switch (style)
{
case PlayButtonStyleActivity:
{
[activityView startAnimating];
}
break;
case PlayButtonStylePause:
{
[activityView stopAnimating];
[playButton setBackgroundImage:[UIImage imageNamed:#"button_pause.png"] forState:UIControlStateNormal];
}
break;
case PlayButtonStylePlay:
default:
{
[activityView stopAnimating];
[playButton setBackgroundImage:[UIImage imageNamed:#"button_play.png"] forState:UIControlStateNormal];
}
break;
}
[self setNeedsLayout];
}
#end
AppDelegate - didFinishLaunching
// setup audio player
audioPlayer = [[AudioPlayerViewController alloc] init]; // public property ...
CGRect frame = self.window.rootViewController.view.frame;
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
CGFloat tabBarHeight = tabBarController.tabBar.frame.size.height;
audioPlayer.view.frame = CGRectMake(0.0f, frame.size.height - tabBarHeight, 320.0f, 40.0f);
[self.window.rootViewController.view insertSubview:audioPlayer.view belowSubview:tabBarController.tabBar];
From any view controller inside the app I start audio with the following code:
- (void)playAudioWithURL:(NSURL *)URL title:(NSString *)title
{
OnsNieuwsAppDelegate *appDelegate = (OnsNieuwsAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.audioPlayer playAudioAtURL:URL withTitle:title];
}
Assets
For the above example, the following assets can be used (button images are white, so hard to see against background):
Buttons:
Background:
There's a lot of discussion (and links to blogs, etc.) about singletons over at What should my Objective-C singleton look like?, and I see a fair number of tutorials as a result of this Google search: http://www.google.com/search?q=+cocoa+touch+singleton+tutorial, but the real answer to your question, I believe, is that you should do one of two things:
If you do want the sound for a particular view to continue playing when the user switches, create the player as you're doing now, but when the view (re)appears, check that a player exists, and don't make a new one.
If you want the sound to stop, then stop the sound when the view changes (i.e., in viewWillDisappear:).