I have a UIPickerView where I have a Male and a Female option.
What I have is when I click on label, the UIPickerView is shown, and based on the selection in UIPickerView, the same text is shown on label.
All is working well, but tapping on UIPickerView is not working properly.
When I tap on UIPickerView,
For iOS6, UIPickerView gets dismissed.
For iOS7, UIPickerView DOESN'T get dismissed.
So what I wanted to do is to not dismiss UIPickerView when I click on it.
Any idea how to do that for iOS 6?
Dropbox link
Code
.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource>
#property (retain, nonatomic) IBOutlet UIPickerView *myPicker;
#property (retain, nonatomic) IBOutlet UILabel *myLabel;
#property (retain, nonatomic) IBOutlet NSMutableArray *arrayGender;
#end
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myPicker, myLabel, arrayGender;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
arrayGender = [[NSMutableArray alloc] init];
[arrayGender addObject:#"Male"];
[arrayGender addObject:#"Female"];
myLabel.text = #"Choose gender...";
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideAllKeyboards)];
tapGesture.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapGesture];
myLabel.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGestureRecognizergenderLabel = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(genderLabelTapped)];
tapGestureRecognizergenderLabel.numberOfTapsRequired = 1;
[myLabel addGestureRecognizer:tapGestureRecognizergenderLabel];
[tapGestureRecognizergenderLabel release];
myPicker.hidden = NO;
}
-(void) genderLabelTapped {
NSLog(#"genderLabelTapped");
[myPicker reloadAllComponents];
myPicker.hidden = NO;
}
-(IBAction)hideAllKeyboards {
NSLog(#"hideAllKeyboards");
myPicker.hidden = YES;
}
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"viewWillAppear");
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) dealloc {
[myPicker release];
[myLabel release];
[super dealloc];
}
- (IBAction)takeMeBack:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark PickerView DataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
NSLog(#"custom data..");
if (IS_DEVICE_RUNNING_IOS_7_AND_ABOVE()) {
// NSLog(#"changing font...");
UILabel *label;
// = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 233, 44)]; // your frame, so picker gets "colored"
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 233, 44)];
label.textColor = [UIColor whiteColor];
label.font = [UIFont fontWithName:#"Trebuchet MS" size:14];
label.textAlignment = NSTextAlignmentCenter;
label.text = [arrayGender objectAtIndex:row];
return label;
} else {
// NSLog(#"changing font...");
UILabel *label;
// = [[UILabel alloc] initWithFrame:CGRectMake(40, 0, 193, 44)];
label = [[UILabel alloc] initWithFrame:CGRectMake(40, 0, 193, 44)];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor blackColor];
label.font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:18];
label.font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:14];
label.text = [arrayGender objectAtIndex:row];
return label;
}
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [arrayGender count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [arrayGender objectAtIndex:row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
myLabel.text = [NSString stringWithFormat:#"%#", [arrayGender objectAtIndex:row]];
myPicker.accessibilityValue = [NSString stringWithFormat:#"%#", [arrayGender objectAtIndex:row]];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
#end
I know because of gesture recognizer, picker is getting hided in iOS6. The problem is why it is not getting hided in iOS7?
Did you write this yourself if you just copy-pasted it from somewhere and you didn't know what it does so you came to Stack Overflow with close to zero research done? By close to zero research, I mean you didn't even read the code...
What do you think these lines do?
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideAllKeyboards)];
tapGesture.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapGesture];
-(IBAction)hideAllKeyboards {
NSLog(#"hideAllKeyboards");
myPicker.hidden = YES;
}
It turns out, when a touch happens (on the pickerview) in iOS7, the view that catches the tap is of class UIPickerTableViewWrapperCell, while in iOS6, it's of class UIPickerTableView (if not tapping on a row) or UITableViewCellContentView (when tapping on a row). My guess is, the later two let the tap pass through as if the tap happened on their superview (in your case, self.view). <- The last sentence is just a guess, not for sure.
The way you can make sure the picker only gets hidden in case the tap happened on self.view is to set a delegate self as delegate to tapGesture, then implement the gestureRecognizer:shouldReceiveTouch method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view == self.view) {
return YES;
} else {
return NO;
}
}
Related
fairly new, and I would need a hand understanding UIPickerViews.
I have created a UIPickerView programmatically for my project:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIPickerView *myPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 375, 200)];
myPickerView.delegate = self;
myPickerView.showsSelectionIndicator = YES;
[self.view addSubview:myPickerView];
}
And then added a method for the number of rows:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSUInteger numRows = 5;
return numRows;
}
Which returns as expected five question marks. I could then go on to create an array to fill those rows etc... but instead I next add another UIPickerView like so:
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIPickerView *myPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 375, 200)];
myPickerView.delegate = self;
myPickerView.showsSelectionIndicator = YES;
[self.view addSubview:myPickerView];
UIPickerView *my2PickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 400, 375, 200)];
my2PickerView.delegate = self;
my2PickerView.showsSelectionIndicator = YES;
[self.view addSubview:my2PickerView];
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSUInteger numRows = 5;
return numRows;
}
Now I have two pickerview controllers and they BOTH have five rows. My question is how do chose to which pickerview the method applies and also could anyone explain why does the method apply to all pickerviews in the project? Thanks.
You only have one delegate method for two PickerViews ; that's something I don't like with iOS but you don't really have a choice here.
You have to if-statement yourself out of this.
The pickerView parameter in the delegate method is the pickerview that is being assigned the number of rows.
Note that this is valid for any of the usual delegate methods for iOS, wether it's the numberOfRows of your pickerview, or your tableview, or your collectionView, or any delegate method that has the view in parameter.
The easy understandable way is to have your pickerview as fields of your class (or properties), and simply compare the parameter with it.
#interface ViewController ()
#property (weak, nonatomic) UIPickerView *_mySexyPickerView;
#property (weak, nonatomic) UIPickerView *_myOtherPickerView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_mySexyPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 375, 200)];
_mySexyPickerView.delegate = self;
_mySexyPickerView.showsSelectionIndicator = YES;
[self.view addSubview:_mySexyPickerView];
_myOtherPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 400, 375, 200)];
_myOtherPickerView.delegate = self;
_myOtherPickerView.showsSelectionIndicator = YES;
[self.view addSubview:_myOtherPickerView];
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (pickerView == _mySexyPickerView){
return 2;
}
if (pickerView == _myOtherPickerView){
return 19;
}
return 0;
}
My below code working fine in below iOS 7
I am trying to add picker view in alert that works fine in below ios7 version But in ios 7 it show white alert without picker view.
-(void)showPinPickerAlert
{
numberarr = [[NSMutableArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9", nil];
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:#"Enter Pin To Change Track me Option" message:#"\n\n\n\n\n\n\n" delegate:self cancelButtonTitle:#"Verify" otherButtonTitles:nil];
UIPickerView *picker=[[UIPickerView alloc]initWithFrame:CGRectMake(25, 30, 230, 60) ];
picker.dataSource=self;
picker.delegate=self;
// picker.backgroundColor=[UIColor blueColor];
picker.showsSelectionIndicator = YES;
// picker.autoresizingMask = UIViewAutoresizingFlexibleHeight;
picker.transform = CGAffineTransformMakeScale(0.6, 0.6);
alert.tag=100;
// picker.transform = CGAffineTransformMakeScale(1, 0.2);
[alert addSubview:picker];
[alert show];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
return 4;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
strPin=[[NSString stringWithFormat:#"%i%i%i%i",[pickerView selectedRowInComponent:0],[pickerView selectedRowInComponent:1],[pickerView selectedRowInComponent:2],[pickerView selectedRowInComponent:3]]mutableCopy];
NSLog(#"strPin=%#",strPin);
// mlabel.text= [arrayNo objectAtIndex:row];
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
{
return [numberarr count];
}
**//code try to change color of picker text and background thats work fine in below ios7 but in iOS 7 still shows white screen**
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *label = (UILabel*) view;
if (label == nil)
{
label = [[UILabel alloc] init];
}
//[label setText:#"Whatever"];
// This part just colorizes everything, since you asked about that.
[label setTextColor:[UIColor whiteColor]];
[label setBackgroundColor:[UIColor blackColor]];
CGSize rowSize = [pickerView rowSizeForComponent:component];
CGRect labelRect = CGRectMake (0, 0, rowSize.width, rowSize.height);
[label setFrame:labelRect];
return label;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
{
pickerView.backgroundColor=[UIColor blackColor];
return [numberarr objectAtIndex:row];
}
OutPut in below
Output in IOS 7
How to fix it?
Adding subviews to a UIAlertView is not supported anymore, starting in iOS7.
You should implement your own view and try to do it similar to the alert view, or like other person said, use a 3rd party alert view like this one:
https://github.com/wimagguc/ios-custom-alertview
Most of the following code is from pre ios7 stack overflow answers.
PickerPrompt.h
#import <UIKit/UIKit.h>
#interface PickerPrompt : UIAlertView <UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate>
{
UIPickerView *_pickerView;
NSMutableArray *_options;
}
#property (readonly) NSString *enteredText;
- (id)initWithTitle:(NSString *)title message:(NSString *)message options:(NSMutableArray*)options delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle okButtonTitle:(NSString *)okButtonTitle;
#end
pickerPrompt.m
#import "PickerPrompt.h"
#implementation PickerPrompt
#define VIEW_TAG 49
#define SUB_LABEL_TAG 52
#define LABEL_TAG 53
#define COMPONENT_WIDTH 250
#define LABEL_WIDTH 10
#synthesize enteredText;
- (id)initWithTitle:(NSString *)title message:(NSString *)message options:(NSMutableArray*)options delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle okButtonTitle:(NSString *)okayButtonTitle
{
if (self = [super initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:okayButtonTitle, nil])
{
_options = options;
_pickerView = [[UIPickerView alloc] init];
[_pickerView sizeToFit];
[_pickerView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[_pickerView setDelegate:self];
[_pickerView setDataSource:self];
[_pickerView setShowsSelectionIndicator:TRUE];
// Change from pre iOS 7
[self setAlertViewStyle:UIAlertViewStylePlainTextInput];
[[self textFieldAtIndex:0] setDelegate:self];
[[self textFieldAtIndex:0] setInputView:_pickerView];
[[self textFieldAtIndex:0] becomeFirstResponder];
}
return self;
}
#pragma mark -
#pragma mark Picker delegate
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (pickerView == _pickerView) {
return [_options count];
}
return [_options count];
}
- (UIView *)labelCellWithWidth:(CGFloat)width rightOffset:(CGFloat)offset {
// Create a new view that contains a label offset from the right.
CGRect frame = CGRectMake(0.0, 0.0, width, 32.0);
UIView *view = [[[UIView alloc] initWithFrame:frame] autorelease];
view.tag = VIEW_TAG;
frame.size.width = width - offset;
UILabel *subLabel = [[UILabel alloc] initWithFrame:frame];
subLabel.textAlignment = UITextAlignmentRight;
subLabel.backgroundColor = [UIColor clearColor];
subLabel.font = [UIFont systemFontOfSize:24.0];
subLabel.userInteractionEnabled = NO;
subLabel.tag = SUB_LABEL_TAG;
[view addSubview:subLabel];
[subLabel release];
return view;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *fullString = [[textField text] stringByAppendingString:string];
for (NSString* object in _options) {
if ([object isEqualToString:fullString]) {
return YES;
}
}
return NO;
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UIView *returnView = nil;
if ((view.tag == VIEW_TAG) || (view.tag == LABEL_TAG)) {
returnView = view;
}
else {
returnView = [self labelCellWithWidth:COMPONENT_WIDTH rightOffset:LABEL_WIDTH];
}
// The text shown in the component is just the number of the component.
NSString *text = [_options objectAtIndex:row];
// Where to set the text in depends on what sort of view it is.
UILabel *theLabel = nil;
if (returnView.tag == VIEW_TAG) {
theLabel = (UILabel *)[returnView viewWithTag:SUB_LABEL_TAG];
}
else {
theLabel = (UILabel *)returnView;
}
theLabel.text = text;
return returnView;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return COMPONENT_WIDTH;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
[[self textFieldAtIndex:0] setText:[_options objectAtIndex:row]];
}
- (NSString *)enteredText
{
return [[self textFieldAtIndex:0] text];
}
#end
How to use (called from an alert view delegate):
PickerPrompt *prompt = [PickerPrompt alloc];
NSMutableArray *options = [[NSMutableArray alloc] initWithObjects:#"option 1", #"option 2", nil];
prompt = [prompt initWithTitle:#"Select Option" message:#"Select Option" options:options delegate:self cancelButtonTitle:#"Cancel" okButtonTitle:#"Okay"];
[prompt show];
[prompt release];
Here is the thing:
I have created a custom NSMenuItem with a custom NSView in it.
Everything works fine except that I can't get the NSMenuItem to get highlighted (= change the background color on mouseover).
I'm trying to do it inside the drawRect method, as shown in other answers posted here.
What am I doing wrong?
The NSView subclass:
#interface customView : NSView
#end
#implementation customView
- (id)initWithFrame:(NSRect)frame
{
NSRect theRect = NSMakeRect(0, 0, 200, 30);
self = [super initWithFrame:theRect];
if (self) {
NSTrackingArea * trackingArea = [[NSTrackingArea alloc] initWithRect:theRect
options: (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow |NSTrackingActiveAlways)
owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
return self;
}
#define menuItem ([self enclosingMenuItem])
- (void) drawRect: (NSRect) rect {
BOOL isHighlighted = [menuItem isHighlighted];
if (isHighlighted) {
//this nslog never happens
NSLog(#"it's highlighted");
}
- (void)mouseUp:(NSEvent*) event {
NSMenuItem* mitem = [self enclosingMenuItem];
NSMenu* m = [mitem menu];
[m cancelTracking];
NSLog(#"you clicked the %ld item",[m indexOfItem: mitem]);
}
#end
The NSMenuItem subclass:
(I add subviews on the custom view here so i can have access to the controls through the NSMenuItem instance)
#interface customItem : NSMenuItem{
}
-(void)setTheText:(NSString*)theString;
#property NSTextField *theLabel;
#end
#import "customItem.h"
#import "customView.h"
#implementation customItem
#synthesize theLabel;
-(id)init{
if (self){
customView *cv = [[customView alloc] init];
theLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 8, 130, 17)];
[theLabel setEditable:NO];
[theLabel setBordered:NO];
NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(170, 7, 20, 20)];
NSButton *myButton1 = [[NSButton alloc] initWithFrame:NSMakeRect(150, 7, 20, 20)];
[myButton setBezelStyle:NSCircularBezelStyle];
[myButton1 setBezelStyle:NSCircularBezelStyle];
[myButton setTitle:#""];
[myButton1 setTitle:#""];
[cv addSubview:myButton];
[cv addSubview:myButton1];
[cv addSubview:theLabel];
[self setView:cv];
[theLabel setStringValue:#"A Value "];
}
return self;
}
-(void)setTheText:(NSString *)theString{
[theLabel setStringValue:theString];
}
#end
And this is the App Delegate :
#interface AppDelegate : NSObject <NSApplicationDelegate>{
NSStatusItem *statusItem;
IBOutlet NSMenu *theMenu;
}
#property (assign) IBOutlet NSWindow *window;
#end
#import "customItem.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
- (void)awakeFromNib{
statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSSquareStatusItemLength];
NSBundle *bundle = [NSBundle mainBundle];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"barIcon" ofType:#"png"]];
NSImage *highlightImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"barIcon_H" ofType:#"png"]];
[statusItem setImage:statusImage];
[statusItem setAlternateImage:highlightImage];
[statusItem setMenu:theMenu];
[theMenu removeAllItems];
customItem *mi = [[customItem alloc] init];
[theMenu addItem:mi];
customItem *mi2 = [[customItem alloc] init];
[theMenu addItem:mi2];
}
#end
This is what i get:
No need to add Booleans or anything else, you can do it from within your custom NSView which is attached to your NSMenuItem
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
//Handle the hightlight
if ([[self enclosingMenuItem] isHighlighted])
{
[self.lbl_title setTextColor:[NSColor whiteColor]];
[self.lbl_amount setTextColor:[NSColor colorWithDeviceRed:151.0f/255.0f green:164.0f/255.0f blue:179.0f/255.0f alpha:1.0f]];
[[NSColor selectedMenuItemColor] setFill];
}
else
{
[self.lbl_title setTextColor:[NSColor blackColor]];
[self.lbl_amount setTextColor:[NSColor whiteColor]];
[[self backgroundColor] setFill];
}
NSRectFill(rect);}
OK i think i got it.
I added a public bool variable in the NSView subclass.
Then i used
-(void)mouseEntered:(NSEvent *)theEvent
and
-(void)mouseExited:(NSEvent *)theEvent
to set the variable to YES or to NO. After setting the variable i used
[self setNeedsDisplay:YES]
to call
-(void) drawRect: (NSRect) rect
That's how i got it working :)
Here is the right way of changing the background colour (highlighting the item) without maintaining a local variable and force the view to draw again,
-(void)mouseEntered:(NSEvent *)event {
self.layer.backgroundColor = [[NSColor blueColor] colorWithAlphaComponent:0.3].CGColor;
[theLabel setTextColor:[NSColor whiteColor]];
}
-(void)mouseExited:(NSEvent *)event {
self.layer.backgroundColor = [NSColor clearColor].CGColor;
[theLabel setTextColor:[NSColor blackColor]];
}
Make sure you also set [self setWantsLayer:YES] before changing the layer background colour.
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:).
Can anyone please suggest some links for using UITextField in cocos2d.
I want to press on label, then the UITextField should get selected and I need to edit on that UITextField.
I'm doing this in a current project to allow for entering the number of the level to start playing at, so that's why my variables and methods are named the way they are; you should probably adjust these to make sense for you.
In your app controller, define this as an instance variable:
UITextField *levelEntryTextField;
Create it inside applicationDidFinishLaunching:
levelEntryTextField = [[UITextField alloc] initWithFrame:
CGRectMake(60, 165, 200, 90)];
[levelEntryTextField setDelegate:self];
Define a method to activate the text field. You should also declare it in the header file for your app controller.
- (void)specifyStartLevel
{
[levelEntryTextField setText:#""];
[window addSubview:levelEntryTextField];
[levelEntryTextField becomeFirstResponder];
}
This will make pressing "return" on the keypad end editing
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
//Terminate editing
[textField resignFirstResponder];
return YES;
}
This is triggered when the editing is actually done.
- (void)textFieldDidEndEditing:(UITextField*)textField {
if (textField==levelEntryTextField) {
[levelEntryTextField endEditing:YES];
[levelEntryTextField removeFromSuperview];
// here is where you should do something with the data they entered
NSString *result = levelEntryTextField.text;
}
}
Now to actually set things in motion, you put this somewhere. I call this from within one of my Scene classes, in response to a user action:
[[[UIApplication sharedApplication] delegate] specifyStartLevel];
I took the example that Jack provided and actually created a working project, this was done using the Cocos2D 0.7.1 XCode Template, and then just editting the *AppDelegate.m/.h files, which are provided below in there entirety. I also modified some of what Jack said, because I feel that creating the UITextField in the appDidFinishLoading would utilize a bit too much memory, especially if the text field is not used all the time ... this solution creates the text field only when it is needed, the sample draws an empty Cocos2D Layer scene, and on screen touch, it displays the text field for you to start entering text into. It will spit out the result of what you entered to the Console - you can pass this to whatever is necessary in your own code.
the .h
#import <UIKit/UIKit.h>
#import "cocos2d.h"
#interface MYSCENE : Layer <UITextFieldDelegate>
{
UITextField *myText;
}
-(void)specificStartLevel;
#end
#interface textFieldTestAppDelegate : NSObject <UIAccelerometerDelegate, UIAlertViewDelegate, UITextFieldDelegate, UIApplicationDelegate>
{
UIWindow *window;
}
#end
and then the .m
#import "textFieldTestAppDelegate.h"
#implementation MYSCENE
-(id) init
{
self = [super init];
isTouchEnabled = YES;
return self;
}
-(BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self specifyStartLevel];
return kEventHandled;
}
-(void)specifyStartLevel {
myText = [[UITextField alloc] initWithFrame:CGRectMake(60, 165, 200, 90)];
[myText setDelegate:self];
[myText setText:#""];
[myText setTextColor: [UIColor colorWithRed:255 green:255 blue:255 alpha:1.0]];
[[[[Director sharedDirector] openGLView] window] addSubview:myText];
[myText becomeFirstResponder];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
[myText resignFirstResponder];
return YES;
}
-(void)textFieldDidEndEditing: (UITextField *)textField {
if(textField == myText) {
[myText endEditing:YES];
[myText removeFromSuperview];
NSString *result = myText.text;
NSLog([NSString stringWithFormat:#"entered: %#", result]);
} else {
NSLog(#"textField did not match myText");
}
}
-(void) dealloc
{
[super dealloc];
}
#end
#implementation textFieldTestAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setUserInteractionEnabled:YES];
[[Director sharedDirector] setDisplayFPS:YES];
[[Director sharedDirector] attachInWindow:window];
Scene *scene = [Scene node];
[scene addChild: [MYSCENE node]];
[window makeKeyAndVisible];
[[Director sharedDirector] runWithScene: scene];
}
-(void)dealloc
{
[super dealloc];
}
-(void) applicationWillResignActive:(UIApplication *)application
{
[[Director sharedDirector] pause];
}
-(void) applicationDidBecomeActive:(UIApplication *)application
{
[[Director sharedDirector] resume];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
[[TextureMgr sharedTextureMgr] removeAllTextures];
}
#end
To add Text field in cocos2d as below code
first of all you add view in Scene and afetr add textfield add in view thats very easy.
-(id) init
{
if( (self=[super init]) )
{
// add view in scene
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 568)];
view.backgroundColor = [UIColor redColor];
// add textfield in view
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 140, 300, 30)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.font = [UIFont systemFontOfSize:15];
textField.placeholder = #"enter text";
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDone;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.delegate = self;
[view addSubview:textField];
// add view in scene
[[[CCDirector sharedDirector] view] addSubview:view];
}
return self;
}
you can also use CCTextfield in cocos2d best example is https://github.com/iNinja/CCTextField
Try the following CCNode subclass, CCMenuItemTextField, to use text fields in cocos2d.
The class is directly subclassed from CCMenuItemSprite. When tapped, the "selected" method is called and a UITextField is added to the main window. After editing is done, "unselected" method is called and the UITextField is removed from screen. User's input is saved to a CCLabelTTF node, which position itself exactly as the original UITextField.
CCMenuItemTextField.h
#interface CCMenuItemTextField : CCMenuItemSprite<UITextFieldDelegate> {
UITextField *textField_;
CCLabelTTF *label_;
CGFloat paddingLeft_;
}
#property (readonly, nonatomic) UITextField *textField;
#property (readonly, nonatomic) CCLabelTTF *label;
#property (assign, nonatomic) CGFloat paddingLeft;
- (void)selected;
- (void)unselected;
- (void)setFontSize:(CGFloat)size;
- (NSString*)text;
- (void)setText:(NSString*)text;
#end
CCMenuItemTextField.m
#import "CCMenuItemTextField.h"
#implementation CCMenuItemTextField
#synthesize
textField = textField_,
label = label_,
paddingLeft = paddingLeft_;
- (id)init
{
CCSprite *normalSprite = [CCSprite spriteWithFile:#"text_field_background.png"];
CCSprite *selectedSprite = [CCSprite spriteWithFile:#"text_field_background.png"];
CCSprite *disabledSprite = [CCSprite spriteWithFile:#"text_field_background.png"];
return [self initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite];
}
- (id)initWithNormalSprite:(CCNode<CCRGBAProtocol> *)normalSprite
selectedSprite:(CCNode<CCRGBAProtocol> *)selectedSprite
disabledSprite:(CCNode<CCRGBAProtocol> *)disabledSprite
{
self = [super initWithNormalSprite:normalSprite
selectedSprite:selectedSprite
disabledSprite:disabledSprite
target:self
selector:#selector(selected)];
if (self) {
paddingLeft_ = 3.0;
textField_ = [[UITextField alloc] init];
[textField_ setTextColor:[UIColor blackColor]];
[textField_ setFont:[UIFont systemFontOfSize:18]];
label_ = [[CCLabelTTF node] retain];
[label_ setAnchorPoint:ccp(0,0.5)];
[label_ setFontSize:18];
[label_ setVisible:NO];
[label_ setColor:ccBLACK];
[self addChild:label_];
}
return self;
}
- (void)dealloc
{
[label_ release];
[textField_ release];
[super dealloc];
}
// --------------------------------
// Public
// --------------------------------
- (void)selected
{
[super selected];
[label_ setVisible:NO];
CGAffineTransform transform = [self nodeToWorldTransform];
float textFieldHeight = textField_.font.lineHeight;
float width = self.contentSize.width;
float height = self.contentSize.height;
float left = transform.tx + paddingLeft_;
float top = 480 - transform.ty - height + (height - textFieldHeight) / 2;
[textField_ setFrame:CGRectMake(left, top, width, height)];
[[[[CCDirector sharedDirector] view] window] addSubview:textField_];
[textField_ becomeFirstResponder];
[textField_ setDelegate:self];
}
- (void)unselected
{
[super unselected];
[label_ setVisible:YES];
[label_ setPosition:ccp(paddingLeft_, self.contentSize.height/2)];
NSString *text = textField_.text ? textField_.text : #"";
[label_ setString:text];
[textField_ resignFirstResponder];
[textField_ removeFromSuperview];
}
- (NSString*)text
{
return [label_ string];
}
- (void)setText:(NSString*)text
{
[label_ setString:text];
[textField_ setText:text];
}
// --------------------------------
// UITextFieldDelegate
// --------------------------------
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
[self unselected];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField*)textField {
[self unselected];
}
- (void)setFontSize:(CGFloat)size
{
[label_ setFontSize:size];
[textField_ setFont:[UIFont systemFontOfSize:size]];
}
// --------------------------------
// CCNode
// --------------------------------
- (void)onExitTransitionDidStart
{
[super onExitTransitionDidStart];
[self unselected];
}
#end