UITextField stopping shake event? - iphone

For those of you who work graphically, here is a diagram of the view hierarchy:
Root View -> UINavigationController -> UITableView -> Edit View -> Problem UITextfield
Basically, - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event; gets called in my Root View when the device shakes.
When a user taps on the "edit" icon (a pen, in the bottom of the screen - not the traditional UINavigationBar edit button), the main view adds a subview to itself and animates it on to the screen using a custom animation.
This subview contains a UINavigationController which holds a UITableView. The UITableView, when a cell is tapped on, loads a subview into itself. This second subview is the culprit. For some reason, a UITextField in this second subview is causing problems.
When a user taps on the view, the main view will not respond to shakes unless the UITextField is active (in editing mode?).
Additional info:
My Motion Event Handler:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(#"%#", [event description]);
SystemSoundID SoundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"shake" ofType:#"aif"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:soundFile], &SoundID);
AudioServicesPlayAlertSound(SoundID);
[self genRandom:TRUE];
}
The genRandom: Method:
/* Generate random label and apply it */
-(void)genRandom:(BOOL)deviceWasShaken{
if(deviceWasShaken == TRUE){
decisionText.text = [NSString stringWithFormat: (#"%#", [shakeReplies objectAtIndex:(arc4random() % [shakeReplies count])])];
}else{
SystemSoundID SoundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"string" ofType:#"aif"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:soundFile], &SoundID);
AudioServicesPlayAlertSound(SoundID);
decisionText.text = [NSString stringWithFormat: (#"%#", [pokeReplies objectAtIndex:(arc4random() % [pokeReplies count])])];
}
}
shakeReplies and pokeReplies are both NSArrays of strings. One is used for when a certain part of the screen is poked and one is for when the device is shaken. The app will randomly choose a string from the NSArray and display onscreen.
As always, code samples are appreciated. I've added my own to help explain the problem.

try using [self.view endEditing:YES];
and for key board to be glassy use:
[textfield setKeyboardAppearance:UIKeyboardAppearanceAlert];
Hope this helps.
Thanks,
Madhup

Keyboard: Look for UIKeyboardAppearanceAlert
As for the shake detection. Once the UITextField resigns first responder (not in focus, keyboard hidden), you need to make sure another UIResponder in the chain becomes firstResponder. For example, if you have a button that sends -resignFirstResponder to the text field, you'll need to do this:
- (IBAction)pressButton {
[textField resignFirstResponder];
[self becomeFirstResponder];
}

Related

Trying to send a specific audio file to a detail view in iOS

I'm fairly new to iOS programming so pardon me if this is an easy issue to fix.
I'm building musical reference app that has a tableview that drills down to a detail. Through the table, when you select a row it sends the detail a picture and text for a label and a scrollview. I'm trying to make it so that I have a custom button on top of the picture and you will be able to tap that picture to play an audio example.
I've figured out how to get the button to play a sound but I can't figure out how to choose which audio file to play.
Here is the seque where I send the Arrays
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowBasicsDetails"]) {
BasicsDetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
detailViewController.basicsDetailModel = [[NSArray alloc]initWithObjects:
[self.basicsNames objectAtIndex:[myIndexPath row]],
[self.basicsImages objectAtIndex:[myIndexPath row]],
[self.basicsDetails objectAtIndex:[myIndexPath row]],
[self.basicsAudios objectAtIndex:[myIndexPath row]],nil];
}
Here is the viewDidLoad from my detail view.
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view.
self.basicsLabel.text = [self.basicsDetailModel objectAtIndex:0];
self.basicsImage.image = [UIImage imageNamed:[self.basicsDetailModel objectAtIndex:1]];
self.basicsDetail.text = [self.basicsDetailModel objectAtIndex:2];
I'm planning on using AVAudioPlayer but all I can find on that pulls in a specific audio file like in this example from techotopia
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"Kalimba"
ofType:#"mp3"]];

Pass specific touch events in UIView/UIWindow to the next responder (sendEvent:/hitTest: doesn't work )

I'm trying to capture all the touch events in a UIKeyboard without breaking its functionality. I've tried:
HitTest:
UIGestureRecognizer
Adding a UIWindow in the top and pass events to next responder
However, none of these worked.
And it seems that we aren't allowed to sub class the UIKeyboard.
Can you think of any method that may work?
Thanks,
Peak
Update:
Lets simplify the problem: How can I add a UIView or UIWindow that passes specific touch events to the next responder(just like setting the userInteractionEnabled to NO)?
Heres my code(of course can't work...):
- (void)sendEvent:(UIEvent *)event {
NSLog(#"%#",event);
//if ([self eventIsNoteworthy:event]) [self extraEventHandling:event];
[super sendEvent:event]; // Apple says you must always call this!
}
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint hitPoint = [underneathButton convertPoint:point fromView:self];
if ([underneathButton pointInside:hitPoint withEvent:event]) return underneathButton;
NSLog(#"Called");
return [super hitTest:point withEvent:event];
//return mainWindow;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Start");
[self.nextResponder touchesBegan:touches withEvent:event];
}
I'm not sure if it's possible or not. A couple of things come to mind. One is to recreate the UIKeyboard yourself and then capture the touches that way. Apple Docs stating it's possible.
The other would be to re-look at how you added the UIWindow overtop of the keyboard. It's my understanding that the keyboard view is somehow layered differently over your app so you might not have had the UIWindow overtop of the keyboard (ref). That link gives a solution for getting a reference to the actual UIView for the keyboard, from there you'd want to place your UIWindow onto the parent of the keyboard view.
To help, here's a script that you can use to dump out all the views to see how they're being layered:
static void dumpViews(UIView* view, NSString *text, NSString *indent)
{
Class cl = [view class];
NSString *classDescription = [cl description];
while ([cl superclass])
{
cl = [cl superclass];
classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
}
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%# %#", classDescription, NSStringFromCGRect(view.frame));
else
NSLog(#"%# %# %#", text, classDescription, NSStringFromCGRect(view.frame));
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViews(subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
Call it like this: dumpViews([[UIApplication sharedApplication] keyWindow], #"", #"");
Hope that helps some! :D
Take a look at this answer about accessing the (private view hierarchy that contains the) UIKeyboard. N.b. this is unsupported in iOS, and Apple rejects apps that do this. (Interfering with private view hierarchies inside UIKit is tantamount to using private API.)

Creating a new keyboard button for iPhone - Bug in InputAccessoryView and InputView

I create a 2 UITextView in my program. They will have to do this:
TextView1 - Will have a new InputView
TextView2 - Will be a normal numeric keyboard with a new button
Because the numeric keyboard of iPhone have a empty button in left of zero. So, I can put a UIView there to simulate a new keyboard. So, I created a UIView with a UIToolbar and this button and set in TextView with a simple
[textView setInputAccessoryView:myView];
To catch a click inside this view is a little more complex. I have to subclass the principalClassName in main.m
int retVal = UIApplicationMain(argc, argv, MyPrincialClass ,MyDelegateClass);
Then create this class with
#interface MyPrincialClass : UIApplication
- (void)sendEvent:(UIEvent *)anEvent;
#end
#implementation MyPrincialClass
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
NSSet *touches = [event allTouches];
UITouch *touch = touches.anyObject;
[[NSNotificationCenter defaultCenter] postNotificationName:#"click" object:(id)touch];
}
#end
Now is only listen this Notification and check if this click is in correct area.
Because I have a UINavegationBar, I have to go to "Next" and "Previous" TextView. OK, this is only run
[textView1 resignFirstResponder];
[textView2 becomeFirstResponder];
If all textView have the same inputView (the keyboard) than the button work normaly. But... Because one of this textView have another inputView, than my problem start.
If I first click in a normal UITextView with a button, than the button is show correct like this. The red square is above the keyboard, so it work.
If the first click is in the UITextView with a customized inputView, the button is show correct again
but now, If navegate with the tool bar (with is a simple resignFirstResponder and becomeFirstResponder) the button will be show under the keyboard
In resume... All the problem in only because I use a custom inputview. If I only use the keyboard, I will not have any problem.
To solve this, is only put the UIView of custom button above the keyboard. I try to get the main UIWindows and send the message:
[[UIApplication sharedApplication].keyWindow bringSubviewToFront:myview];
But not work. Any idea to solve this bug??
I Hope i understand you correctly, and i would suggest you to manage Z Position of the button. So that It can appear above all the component. For easy doing i suggest you the following open source,Manage Z Order of Views.
Hope this will help you, if not please let me know .
Cheers,
Arun.
I fix it. The idea was find the UIView of keyboard and add the button like a subview. This was the codes:
- (void)keyboardDidShow {
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(UIView* potentialKeyboard in tempWindow.subviews) {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
if([[potentialKeyboard description] hasPrefix:#"<UIPeripheralHost"] == YES) {
keyboard = potentialKeyboard;
} else {
if([[potentialKeyboard description] hasPrefix:#"<UIKeyboard"] == YES) {
keyboard = potentialKeyboard;
}
}
}
}
[keyboard addSubview:key];
[keyboard bringSubviewToFront:key];
}

UITextView has no events

I am developing an iPhone application which requires a multiline text field (UITextView) to capture some text.
When the user touches inside the textView it becomes firstResponder and displays the keyboard. What I really need it to do is remove the keyboard when the user is finished. Normally with a text field the return/done button press would signal the end of typing and I would use the delegate to resign first responder. However with a multiline textview I want the user to be able to add a new line so that is not possible.
My next option would be to resign first responder if there is a touch up outside of the text view. The problem here is that there are no events declared on UITextView which I can see in interface builder.
How can I create a multiline text field in an iPhone app which will release first responder at a sensible time when the user is done with it?
You can use a DONE button on the navigation bar:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneAction:)];
}
to dismiss the keyboard, handle the event with something like:
- (void) doneAction: (id) sender{ [textView resignFirstResponder]; }
(remember .h file declarations)
I'm guessing you are lamenting the "missing" Done button on the keyboard which is return on a multi line UItextView, correct me if i'm wrong.
The obvious approach seems to have another button on your interface that resigns first responder.
I don't think some clever code that hides the keyboard upon some event would be very intuitive to use.
The Apple Notes application uses its top right done button to resign first responder, an approach like this seems sensible.
//If user touches outside of the keypad, then the keypad will go away
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (touch.tapCount >= 1) {
[ZLabel resignFirstResponder];
}
}
You can create an invisible button that covers all screen and than add a method to it. I think sth like this will work.
- (IBAction)backgroundClick:(id)sender{
[textView resignFirstResponder];
}
If you are completely set on having the 'done' key on the keyboard, the method I use is check the last character in the UITextView when the contents changes. If the last character is a new line, then the user has hit the done button. I would second Neil's view that there should be another button, but sometimes the customer gets what the customer wants.
Obviously you will have to set the delegate for the UITextView to point to the class in which this method lives. The - (void)textViewDidChange:(UITextView *)inTextView method is part of the UITextViewDelegate protocol.
- (void)textViewDidChange:(UITextView *)inTextView {
NSString *text = inTextView.text;
if ([text length] > 0 && [text characterAtIndex:[text length] -1] == '\n') {
message = [text substringToIndex:[text length] -1];
[delegate messageEdited];
[self removeFromSuperview];
}
}
(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
[txtView resignFirstResponder];
return YES;
}

iPhone UISearchBar & keyboardAppearance

When the keyboard appears, I want to set the
keyboardAppearance = UIKeyboardAppearanceAlert
I've checked the documentation and it looks like you can only change the keyboardType.
Can this be done without violating any of Apple's private API's ?
The best way that I found to do this, if you want to do it throughout your app, is to use Appearance on UITextField. Put this in your AppDelegate on launch.
[[UITextField appearance] setKeyboardAppearance:UIKeyboardAppearanceDark];
This should do it:
for(UIView *subView in searchBar.subviews)
if([subView isKindOfClass: [UITextField class]])
[(UITextField *)subView setKeyboardAppearance: UIKeyboardAppearanceAlert];
didn't find any other way of doing...
This no longer works on iOS 7 because the UISearchBar view hierarchy has changed. The UITextView is now a subview of the first subview (e.g. its in the searchBar.subviews[0].subviews array).
A more future proof way to do this would be to check recursively the entire view hierarchy, and to check for UITextInputTraits protocol rather than UITextField, since that is what actually declares the method. A clean way of doing this is to use categories. First make a category on UISearchBar that adds this method:
- (void) setKeyboardAppearence: (UIKeyboardAppearance) appearence {
[(id<UITextInputTraits>) [self firstSubviewConformingToProtocol: #protocol(UITextInputTraits)] setKeyboardAppearance: appearence];
}
Then add a category on UIView that adds this method:
- (UIView *) firstSubviewConformingToProtocol: (Protocol *) pro {
for (UIView *sub in self.subviews)
if ([sub conformsToProtocol: pro])
return sub;
for (UIView *sub in self.subviews) {
UIView *ret = [sub firstSubviewConformingToProtocol: pro];
if (ret)
return ret;
}
return nil;
}
You can now set the keyboard appearance on the search bar in the same way you would a textfield:
[searchBar setKeyboardAppearence: UIKeyboardAppearanceDark];
keyboardAppearance is a property of the UITextInputTraitsProtocol, which means that the property is set via the TextField object. I'm not aware of what an Alert Keyboard is, from the SDK it's a keyboard suitable for an alert.
here's how you access the property:
UITextField *myTextField = [[UITextField alloc] init];
myTextField.keyboardAppearance = UIKeyboardAppearanceAlert;
Now when the user taps the text field and the keyboard shows up, it should be what you want.