iOS unfortunately doesn't have a dropdown picker like html does with the tag. I decided that I was finally going to create one for my app, and it looks and works great. My dropdown object is a subclass of UITextField. However, I changed something and now it only works some of the time.
User interaction is enabled, but I don't want the textfield to be editable. The class in which my dropdown subclass resides is UITextField delegate, and should receive delegate methods for UITextField.
I have - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{ where I check to see if the textfield in question is a dropdown menu, and if it is, I call a method to instantiate a popover and disable editing, but the dropdown only appears on every other tap.
For example, i'll tap the "textfield" and my popover displays. I tap out so the popover goes away, then I tap on the "textfield" and nothing happens. I tap on the textfield once again and the popover appears. No idea why this is happening, here is what i'm doing:
.h
subclass : UIViewController<UITextFieldDelegate>
.m
dropdownTextField.delegate = self;
...
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(textField == self.measurementSelect){
NSLog(#"IM CALLED");
[self showPopover:textField];
return NO;
}
return YES;
}
-(void)showPopover:(id)sender{
if (_measurementPicker == nil) {
_measurementPicker = [[iPadMeasurementSelect alloc] initWithStyle:UITableViewStylePlain];
_measurementPicker.delegate = self;
}
if (_measurementPopover == nil) {
_measurementPopover = [[UIPopoverController alloc] initWithContentViewController:_measurementPicker];
[_measurementPopover presentPopoverFromRect:self.measurementSelect.frame inView:self.conversionView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
else {
[_measurementPopover dismissPopoverAnimated:YES];
_measurementPopover = nil;
}
}
Every tap gets nslogged, so I assume my popover method is the culprit of this problem. Any ideas?
Let's rewrite by teasing apart existence of the UI elements and the visible state of the popover:
// canonical lazy getters for UI elements
- (iPadMeasurementSelect *)measurementPicker {
if (!_measurementPicker) {
_measurementPicker = [[iPadMeasurementSelect alloc] initWithStyle:UITableViewStylePlain];
_measurementPicker.delegate = self;
}
return _measurementPicker;
}
- (UIPopoverController *)measurementPopover {
if (!_measurementPopover) {
_measurementPopover = [[UIPopoverController alloc] initWithContentViewController:self.measurementPicker];
}
return _measurementPopover;
}
// now the show/hide method makes sense. it can take a bool about whether to show or hide
-(void)showPopover:(BOOL)show {
if (show) {
[self.measurementPopover presentPopoverFromRect:self.measurementSelect.frame inView:self.conversionView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
} else {
[self.measurementPopover dismissPopoverAnimated:NO];
// if you want/need to create a new one each time it is shown, nil the popover here, like this:
// self.measurementPopover = nil;
}
}
When the textField begins editing, show the popover like this:
[self showPopover:YES];
And when the delegate gets the didEndEditing message:
[self showPopover:NO];
Related
I have a view controller that's instantiated from IB. It contains a UIButton whose action creates a UIPopoverController whose delegate updates the title of the UIButton through:
- (void) popoverSelected:(NSString*)string {
[self.sortButton setTitle:string forState:UIControlStateNormal];
[self.sortPickerPopover dismissPopoverAnimated:YES];
}
popoverSelected is a delegate method for the UIPopoverController, which contains a simple UITableView.
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selectedSort = [_sortTypes objectAtIndex:indexPath.row];
if (_delegate != nil) {
[_delegate popoverSelected:selectedSort];
}
}
The popover is instantiated by the TouchUpInside action on the self.button through:
- (IBAction)sortButtonPressed:(id)sender {
if (_sortPicker == nil) {
// Create the picker view controller
_sortPicker = [[SortPickerViewController alloc] initWithStyle:UITableViewStylePlain];
// Set this as the delegate
_sortPicker.delegate = self;
}
if (_sortPickerPopover == nil) {
// The colour picker popover is not showing. Show it
_sortPickerPopover = [[UIPopoverController alloc] initWithContentViewController:_sortPicker];
[_sortPickerPopover presentPopoverFromRect:_sortButton.frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
} else {
// if it's showing, we want to hide it
[_sortPickerPopover dismissPopoverAnimated:YES];
_sortPickerPopover = nil;
}
}
This has no issues the first time the button's title is updated, but second time around I get an EXC_BAD_ACCESS when executing setTitle: in popoverSelected.
I can't see anywhere that I'm releasing the button accidentally (and the object definitely still exists at this point). The project is using ARC.
With NSZombies I've occasionally reached [__NSArrayI valueRestriction] unrecognised selector sent to instance which makes even less sense.
Are there any obvious approaches I can take to debug this further?
Instead of checking _sortPickerPopover == nil to know whether to show it, you should check [_sortPickerPopover isPopoverVisible]. Also, I would put the construction code into autoloaders.
- (UIPopoverController *)sortPickerPopover
{
if (!_sortPickerPopover) {
_sortPickerPopover = [[UIPopoverController alloc] initWithContentViewController:self.sortPicker];
}
return _sortPickerPopover;
}
- (SortPickerViewController *)sortPicker
{
if (!_sortPicker) {
_sortPicker = [[SortPickerViewController alloc] initWithStyle:UITableViewStylePlain];
// Set this as the delegate
_sortPicker.delegate = self;
}
return _sortPicker;
}
- (IBAction)sortButtonPressed:(UIButton *)sender
{
if ([self.sortPickerPopover isPopoverVisible]) {
[self.sortPickerPopover dismissPopoverAnimated:YES];
} else {
[self.sortPickerPopover presentPopoverFromRect:sender.frame
inView:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
/***
* NOTE: Delegate methods should always pass the calling object as the first
* object. Additionally, the name is not very descriptive of what is actually
* being performed and does not use should/will/did naming conventions.
* You should consider changing this method to something like:
* - (void)sortPickerViewController:(SortPickerViewController *)sortPicker
* didSelectSortMethod:(NSString *)sortMethod
**/
- (void)popoverSelected:(NSString *)string
{
[self.sortButton setTitle:string forState:UIControlStateNormal];
[self.sortPickerPopover dismissPopoverAnimated:YES];
}
Once these changes are made, the only other possible source of problems is the implementation of your SortPickerViewController. I'll look that over for you if you can post that view controller as well.
This following code is to scroll view up. However, it is not shifting up my viewcontoller. I have debugged code and it hits every single line of code , but my view controller. is not shifting up
//DownViewController.m
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField
{
if (textField == self.hoursTextField) {
[textField resignFirstResponder];
taggy=self.hoursTextField.tag;
}
if (textField ==self.codeTextField) {
[textField resignFirstResponder];
taggy=self.codeTextField.tag;
}
return YES;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
//Assign new frame to your view
sensorViewController =[[SensorsViewController alloc]initWithNibName:#"SensorsViewController" bundle:[NSBundle mainBundle]];
[sensorViewController setTag:taggy];
}
//SensorsViewController.m
-(void)setTag: (int)tag
{
if(tag==1)
{
a=-80;
[self.view setFrame:CGRectMake(0,a,1030,768)];
}
if(tag==2)
{
a=-260;
[self.view setFrame:CGRectMake(0,a,1030,768)];
}
}
Shift the call of setTag method inside viewDidLoad of SensorsViewController.
You are assigning a new value to sensorViewController every time the keyboard shows. That instance that you just assigned isn't onscreen, so of course the one that is onscreen won't be affected. You should keep a reference to the view controller that's already onscreen, maybe with an IBOutlet if you're using a storyboard or nib file.
What I have :
TextView
NSArray (string)
AVAudioplayer (not yet implemented)
When I select a word in TextView :
• Check if word exist in Array
• Start Audioplayer with associated sound
Unfortunately when I tap twice to select a word inside TextView, textViewDidChangeSelection is called twice. I don’t know why I see "Youpie" twice.
I just changed inputView to hide keyboard because I only need TextView to be used in selecting mode.
- (void)textViewDidChangeSelection:(UITextView *)tve;
{
NSString *selectedText = [tve textInRange:tve.selectedTextRange];
if(selectedText.length > 0)
{
for (NSString *text in textArray)
{
if ([selectedText isEqualToString:text])
NSLog(#"Youpie");
tve.selectedTextRange = nil;
if (ps1.playing == YES)
{
[self stopEveryPlayer];
[self updateViewForPlayerState:ps1];
}
else if ([ps1 play])
{
[self updateViewForPlayerState:ps1];
fileName.text = [NSString stringWithFormat: #"%# (%d ch.)", [[ps1.url relativePath] lastPathComponent], ps1.numberOfChannels, nil];
}
else
NSLog(#"Could not play %#\n", ps1.url);
break;
}
}
}
}
- (void)awakeFromNib
{
textArray = [[NSArray alloc] initWithObjects:#"dog",#"cat",#"person",#"bird",#"mouse", nil];
textView.inputView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
textView.delegate = self;
// ...
}
I noticed something when I was double tapping on each good word in my text.
textViewDidChangeSelection
If a word is already selected and no action choosen, I have 1 "Youpie".
If not, I have 2 "Youpie".
I found a simple solution. I removed selectedRange after getting value. textViewDidChangeSelection called once.
What I have changed
tve.selectedTextRange = nil;
I use a subclass of UITextView to disable menu.
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return NO;
return [super canPerformAction:action withSender:sender];
}
I added an implementation for AVAudioPlayer (ps1) too.
My "autoplay" is working if a known word is selecting :)
I don't have an answer for why the method gets called twice or how to prevent this, but an alternative solution might be to display an additional item in the edit menu that pops up in a text view when a word is double clicked. Then, your action for initiating a sound based on the word could be triggered from the action selector defined in that additional menu item. In this design, you'd remove your textViewDidChangeSelection and thus would not get called twice. See http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/AddingCustomEditMenuItems/AddingCustomEditMenuItems.html for some additional info about modifying the standard menu.
I am trying to invoke -(BOOL) textFieldShouldClear:(UITextField *)textField when UITextField's clear button is tapped. I have already set my delegate and UITextField's other delegate's methods are being called correctly, except this one. Clear Button is set to "is always visible" in nib file.
EDIT
FYI I am showing FPPopover when textfield's text is changed. if I tap on clear button without showing popover, clear button works fine. But if I try to tap it when popover is being displayed, delegate method is not called.
Code Snippet
-(BOOL) textFieldShouldClear:(UITextField *)textField
{
return YES;
}
- (IBAction)didChangeScripText:(id)sender {
NSString *text = isPortrait ? symbolTextField.text : landsymbolTextfield.text;
if(scripList.count == 0)
{
if([Logs sharedManager].scripData.count > 0)
[self extractScrips];
else
return;
}
// SAFE_ARC_RELEASE(popover);
// popover=nil;
//the controller we want to present as a popover
if(controller == nil)
controller = [[scripComboViewController alloc] initWithStyle:UITableViewStylePlain];
if(controller.scripListFiltered.count > 0)
[controller.scripListFiltered removeAllObjects];
controller.delegate = self;
if(popover == nil){
popover = [[FPPopoverController alloc] initWithViewController:controller];
popover.tint = FPPopoverDefaultTint;
}
controller.scripListFiltered = [NSMutableArray arrayWithArray:[scripList filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",text]]];
NSLog(#"array is: %#",controller.scripListFiltered);
if(controller.scripListFiltered.count == 0)
{
[popover dismissPopoverAnimated:YES];
return;
}
//decide contentsize and arrow dir based on tableview height
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
popover.contentSize = CGSizeMake(300, 500);
}
else {
popover.contentSize = CGSizeMake(200, 200);
}
//sender is the uitextfield
float height = isPortrait ? portTable.frame.size.height : landTable.frame.size.height;
if(height > 0)
popover.arrowDirection = FPPopoverArrowDirectionDown;
else
popover.arrowDirection = FPPopoverArrowDirectionUp;
if(![popover isModalInPopover])
[popover presentPopoverFromView:sender];
[controller reloadTable];
}
What is going wrong? Can anyone tell me. Thanks.
Actually problem is due to FPPopover. When it receives touch event outside its view, it dismisses itself, and no interaction with outside controls is possible at that time. So if tap clear button, it will be used to dismiss the pop up and then I am able to use clear button. Thats all.
In my app I have a view that is a form that has quite a few inputs.
When the UITextField calls textFieldDidBeginEditing, it checks the tag and will bring up a UIPopoverController or the keyboard depending on the what the input is meant to be.
If the keyboard is up, I need it disappear when the user presses a textfield that brings up the popover. However I cannot make it disappear, I have tried every way to get rid of the keyboard but it just stays there. I have tried:
calling resignFirstResponder in textFieldDidEndEditing
calling [self.view endEditing:YES] in textFieldDidEndEditing
calling resignFirstResponder AND [self.view endEditing:YES] in textFieldDidBeginEditing checking for the previous tag is equal to a keyboard input text field.
Any ideas would be great.
I have ripped it out and and put it in a example project if anyone wants to see the exact behaviour.
http://dl.dropbox.com/u/61692457/KB_Test.zip
Declare a Global UITextField in .h file
UITextField *txtfld;
Replace Your method textFieldDidBeginEditing with textFieldShouldBeginEditing and now write this code
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if (textField.tag == 1 || textField.tag==3)
{
if(numPickerPopover == nil)
{
numPicker = [[[NumPicker alloc] initWithStyle:UITableViewStylePlain] autorelease];
numPicker.delegate = self;
numPickerPopover = [[UIPopoverController alloc] initWithContentViewController:numPicker];
[numPickerPopover setPopoverContentSize:CGSizeMake(60.0, 260.0f)];
}
[numPickerPopover presentPopoverFromRect:textField.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[txtfld resignFirstResponder];
return NO;
}
if (textField.tag == 2)
{
txtfld = textField;
return YES;
}
return YES;
}
To dismiss the keyboard when the user touches the textField that brought it up, add this method:
- (IBAction)dismissKeyboard:(id)sender {
[textField resignFirstResponder];
}
In Interface Builder, connect this method to the textField event you want, like touch up inside (or whatever is more appropriate).