I have a dynamic number of UITextFields which all have an inputAccessoryView. This is composed of a UIToolbar which has a button on it. When this button is pressed it calls a method, however, within this method I somehow need the ability to access the underlying UITextField.
Please could someone advise how this is possible?
// Create the toolbar controls
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(navigationBarDoneButtonPressed:)];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
// Setup the toolbar
navigationToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(10.0, 0.0, 310.0, 40.0)];
[navigationToolbar setItems:#[flexibleSpace, doneButton]];
// In a UITableView I create a number of cells which contain UITextFields....
// This is the method that gets called when a user presses the done button
- (void)navigationBarDoneButtonPressed:(id)sender
{
NSLog(#"Based on sender I need to access the underlying UITextField.");
}
You can keep track of the currently focused UITextField in a variable, and use that variable in your inputAccesoryView method:
in your .h file: make sure you're conforming to the UITextFieldDelegate protocol:
#interface MyViewController : UIViewController <UITextFieldDelegate>
and add this property:
#property (assign, nonatomic) UITextField *activeTextField;
In your .m file: While creating your dynamic textfields:
...
theTextField.delegate=self;
...
And add these implementations of the UITextFieldDelegate protocol:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.activeTextField = nil;
}
And now, in the method invoked by your inputAccesoryView:
- (void)navigationBarDoneButtonPressed:(id)sender{//Just an example
self.activeTextField.text=#"Test";
}
I would create UITableViewCell subclass with delegate, e.g.
MyTextEditTableViewCell.h
#class MyTextEditTableViewCell;
#protocol MyTextEditTableViewCellDelegate <NSObject>
- (void)didDoneButtonPressed:(MyTextEditTableViewCell *)sender;
#end
#interface MyTextEditTableViewCell : UITableViewCell
#property (nonatomic, weak) id<MyTextEditTableViewCellDelegate> delegate;
#property (nonatomic, strong) UITextField *textField;
#end
MyTextEditTableViewCell.m
#implementation MyTextEditTableViewCell
- (void)navigationBarDoneButtonPressed:(id)sender
{
// Call delegate
if (self.delegate)
[self.delegate didDoneButtonPressed:self];
}
#end
Your UIViewController subclass (Or UITableViewController)
...
- (void)didDoneButtonPressed:(MyTextEditTableViewCell *)sender {
UITextField *cellTextField = sender.textField;
// do some stuff with text field
}
Don't forget to set delegate to cell when you will config it.
Related
I have a custom UIButton class like below :
.h
#import <UIKit/UIKit.h>
#class FriendButton;
#protocol LongPressedButtonDelegate
- (void)buttonIsLongPressed:(FriendButton *)button;
#end
#interface FriendButton : UIButton
#property (nonatomic, weak) id<LongPressedButtonDelegate > delegate;
#end
.m
#implementation FriendButton
//this is called from the interface builder
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:(NSCoder *)aDecoder];
NSLog(#"init called");
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPress:)];
[self addGestureRecognizer:longPress];
return self;
}
-(void)longPress:(id)sender {
NSLog(#"long press");
[self.delegate buttonIsLongPressed:self];
}
#end
My buttons are set up in the interface builder, they are contained in a UITableView cell. In my UITableViewController I have :
-(void)buttonIsLongPressed:(FriendButton *)button {
NSLog(#"Delegate method!");
}
and it controller conforms to protocol. The long press gesture works but the delegate method is not being called. I'm not sure why it's not working. Is it because I have to set each buttons delegate to the UITableViewController? If so how would I do that? The buttons are set up in the interface builder.
Define your UIButton property this way
#interface FriendButton : UIButton
#property (nonatomic, weak) IBOutlet id<LongPressedButtonDelegate > delegate;
#end
Then go to Interface Builder and right click on UIButton and you will see delegate link this delegate to UITableViewController. It should work
In your cellForRowAtIndexPath implementation in your UITableViewController you will need to do something like:
cell.button.delegate = self;
Edit: This is if you want to do it programmatically. Refer to the answers around IBOutlets if you want to do it in your storyboard.
I have a total of 5 text fields in a view controller. 1 of them opens the keyboard to fill in data. 2 of them open up the picker view to select the value. 1 opens up the Date Picker. And the last one segues to a Table View Controller.
The pickers and keyboard open up fine as I transition from one text field to another. If I click on the text field to segue to the table view, it opens as well. The problem occurs when I try to open the Table View if there is currently a picker or keyboard already active. The picker or keyboard from the previous selected text field shows in front of the table view. How do I hide the picker or keyboard before going to the table view?
Link to sample project http://www.filedropper.com/test_9
.h file
#import <UIKit/UIKit.h>
#interface AddAEntryViewController : UIViewController<UITextFieldDelegate,UIPickerViewDataSource,UIPickerViewDelegate>
#property (weak, nonatomic) IBOutlet UITextField *textEntryName;
#property (weak, nonatomic) IBOutlet UITextField *textEntryStatus;
#property (weak, nonatomic) IBOutlet UITextField *textEntryLocation;
#property (weak, nonatomic) IBOutlet UITextField *textEntryGender;
#property (weak, nonatomic) IBOutlet UITextField *textDOB;
#property (weak, nonatomic) IBOutlet UIPickerView *pickerAddEntryInfo;
#property (weak, nonatomic) IBOutlet UIDatePicker *datepickerAddEntryInfo;
#end
.m file
#import "AddAEntryViewController.h"
#interface AddAEntryViewController ()
#property(strong, nonatomic)NSArray *arrayEntryStatus;
#property(strong, nonatomic)NSArray *arrayEntryLocation;
#property(strong, nonatomic)NSArray *arrayEntryGender;
#end
#implementation AddAEntryViewController{
}
#synthesize textEntryName=_textEntryName;
#synthesize textEntryStatus = _textEntryStatus;
#synthesize textEntryLocation = _textEntryLocation;
#synthesize textEntryGender = _textEntryGender;
#synthesize textDOB =_textDOB;
#synthesize pickerAddEntryInfo = _pickerAddEntryInfo;
#synthesize datepickerAddEntryInfo = _datepickerAddEntryInfo;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//TOOLBAR SETUP
CGRect cgRect =[[UIScreen mainScreen] bounds];
CGSize cgSize = cgRect.size;
UIToolbar* toolbar = [[UIToolbar alloc] init];
toolbar.frame=CGRectMake(0, 0, cgSize.width, 35);
toolbar.barStyle = UIBarStyleBlackTranslucent;
//PICKER SETUP
_pickerAddEntryInfo.hidden=YES;
_arrayEntryStatus = [[NSArray alloc]initWithObjects:#"Single",
#"Married", nil];
_arrayEntryGender = [[NSMutableArray alloc] initWithObjects:#"Female",#"Male", nil];
_textEntryStatus.inputView = _pickerAddEntryInfo;
_textEntryGender.inputView = _pickerAddEntryInfo;
_textEntryStatus.inputAccessoryView=toolbar;
_textEntryGender.inputAccessoryView=toolbar;
//DATEPICKER SETUP
_datepickerAddEntryInfo.hidden=YES;
[_datepickerAddEntryInfo setMaximumDate:maxDate];
[_datepickerAddEntryInfo setMinimumDate:minDate];
[_datepickerAddEntryInfo setDate:startDate];
_textDOB.inputView = _datepickerAddEntryInfo;
_textDOB.inputAccessoryView = toolbar;
}
#pragma mark - Text Field Editing Begins
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
//Picker - these text fields open up the Picker View
if (_textEntryStatus.editing == YES || _textEntryGender.editing == YES)
{
_pickerAddEntryInfo.hidden=NO;
}
//DatePicker - this text field opens up a Date Picker
if (_textDOB.editing == YES)
{
_datepickerAddEntryInfo.hidden=NO;
}
//TableView for Entry Location - This text field will segue to a Table View Controller populated with data
if (_textEntryLocation.editing == YES)
{
[textField resignFirstResponder];
}
}
#pragma mark - Text Field Editing Ends
-(void) textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
Use prepareForSegue: to perform any actions before segueing to another view controller.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"segueToMyTableView"]) {
// send resignFirstResponder to all textFields
[textEntryName resignFirstResponder];
[textEntryStatus resignFirstResponder];
[textEntryLocation resignFirstResponder];
[textEntryGender resignFirstResponder];
[textDOB resignFirstResponder];
// and remove all pickers views from the superview
[pickerAddEntryInfo removeFromSuperview];
[datepickerAddEntryInfo removeFromSuperview];
}
}
You can assign name to your segue in Storyboard like this (don't forget to select it on scheme)
Related documentation is here.
I'm trying to use a Button in my UIPopover to create a UITextView in my Main UIViewController the code I have looks something like this (PopoverView.h file):
#protocol PopoverDelegate <NSObject>
- (void)buttonAPressed;
#end
#interface PopoverView : UIViewController <UITextViewDelegate> { //<UITextViewDelegate>
id <PopoverDelegate> delegate;
BOOL sendDelegateMessages;
}
#property (nonatomic, retain) id delegate;
#property (nonatomic) BOOL sendDelegateMessages;
#end
Then in my PopoverView.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton * addTB1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
addTB1.frame = CGRectMake(0, 0, 100, 50);
[addTB1 setTitle:#"Textbox One" forState:UIControlStateNormal];
[self.view addSubview:addTB1]; // Do any additional setup after loading the view from its nib.
[addTB1 addTarget:self action:#selector(buttonAPressed)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)buttonAPressed
{
NSLog(#"tapped button one");
if (sendDelegateMessages)
[delegate buttonAPressed];
}
And also in my MainViewController.m :
- (void)buttonAPressed {
NSLog(#"Button Pressed");
UITextView *textfield = [[UITextView alloc] init];
textfield.frame = CGRectMake(50, 30, 100, 100);
textfield.backgroundColor = [UIColor blueColor];
[self.view addSubview:textfield];
}
I'm using a delegate protocol to link the popover and the ViewController but I'm stuck on how I get my BOOL statement to link the -(void)buttonAPressed in the PopoverView and MainViewController so that when I press the button in the Popover a textview appears in the Main VC. How would I go about doing this?
In MainViewController, where you create PopoverView, be sure to set its delegate property otherwise sending messages to delegate in PopoverView will do nothing.
For example, in MainViewController.m:
PopoverView *pov = [[PopoverView alloc] initWithNibName:nil bundle:nil];
pov.delegate = self; // <-- must set this
thePopoverController = [[UIPopoverController alloc] initWithContent...
I am not sure why you need the sendDelegateMessages variable. Even with that bool, you must set the delegate property so PopoverView has an actual object reference to send the messages to.
If you want to make sure the delegate object has implemented the method you're about to call, you can do this instead:
if ([delegate respondsToSelector:#selector(buttonAPressed)])
[delegate buttonAPressed];
Also, the delegate property should be declared using assign (or weak if using ARC) instead of retain (see Why use weak pointer for delegation? for an explanation):
#property (nonatomic, assign) id<PopoverDelegate> delegate;
Another thing is if you're not using ARC, you need to add [textfield release]; at the end of the buttonAPressed method in MainViewController to avoid a memory leak.
I am trying to access my UIButton in my Play class from my CCLayerClass.
The problem is that it is not working!
Here is how I declare it in the Play class:
.h
IBOutlet UIButton *pauseButton;
#property(nonatomic, retain) IBOutlet UIButton *pauseButton;
.m
#synthesize pauseButton;
Then in the dealloc:
[pauseButton release];
Also of course I connect it then in Interface builder.
Then in my other class (My CCLayer) class. I try to do this:
Play *play = [[[Play alloc] init] autorelease];
[play.pauseButton setHidden:YES];
The thing is, is that it simply just does not hide the button. Is there any reason for this?
Thanks!
Edit1:
My Play.h
IBOutlet UIButton *pauseButton;
BOOL pauseButtonVisible;
#property(nonatomic, retain) IBOutlet UIButton *pauseButton;
#property(readwrite) BOOL pauseButtonVisible;
.m
#synthesize pauseButton;
- (void)setPauseButtonVisible: (BOOL) variableToSet {
pauseButtonVisible = variableToSet;
if(pauseButton)
[pauseButton setHidden: !pauseButtonVisible];
}
- (BOOL) pauseButtonVisible
{
return(pauseButtonVisible);
}
viewWillAppear:
[pauseButton setHidden: !pauseButtonVisible];
I also made sure I connected it in Interface Builder
Then in CCLayerClass I do this:
Play *play = [[[Play alloc] init] autorelease];
if(play.pauseButton == NULL) {
NSLog( #"pause button is NULL");
}
But that NSLog gets called! Why is my pauseButton NULL? I just need to alloc it so it stays alive, is that possible?
Thanks!
play.pauseButtonVisible = YES;
Okay. Hopefully third time is the charm (and after that, I'm giving up cause it's time for me to go to bed).
Here in the .h file, I'm keeping the new pauseButtonVisible BOOL property.
#interface Play : UIViewController
{
BOOL pauseButtonVisible;
IBOutlet UIButton *pauseButton;
}
#property(nonatomic, retain) IBOutlet UIButton *pauseButton;
#property(readwrite) BOOL pauseButtonVisible;
#end
But in the .m file, we're doing something a little different:
#interface Play
// here we are rolling our own setters and getters
// instead of #synthesizing...
- (void)setPauseButtonVisible: (BOOL) variableToSet
{
pauseButtonVisible = variableToSet;
if(pauseButton)
[pauseButton setHidden: !pauseButtonVisible];
}
- (BOOL) pauseButtonVisible
{
return(pauseButtonVisible);
}
- (void) viewWillAppear: (BOOL) animated
{
[pauseButton setHidden: !pauseButtonVisible];
[super viewWillAppear: animated];
}
and
Play *play = [[[Play alloc] init] autorelease]; // you should really be using initWithNibName, but anyways
play.pauseButtonVisible = YES;
So now, hopefully pause button will be visible or hidden at the appropriate time for while your code is running.
First my .h file:
#interface SettingsViewController : UIViewController <UINavigationControllerDelegate>
{
IBOutlet UISwitch *gravaSwitch;
...
}
#property (retain, nonatomic) UISwitch *gravaSwitch;
...
#end
My viewDidload in .m file (it works):
...
// SET SWITCH BUTTON STATE
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TrafficApp" accessGroup:nil];
if ( [[keychain objectForKey:(id)kSecAttrAccount] isEqualToString:#"" ] )
[self.gravaSwitch setOn:FALSE];
else [self.gravaSwitch setOn:TRUE];
...
But my switchChanged doesn't work and I don't know why. On IB everything is right connected, it enters in this method but gravaSwitch is always null.
- (IBAction)switchChanged:(id)sender
{
if ( self.gravaSwitch.on )
{
NSLog(#"IF");
[self.gravaSwitch setOn:FALSE animated:YES];
}
else
{
NSLog(#"ELSE");
[self.gravaSwitch setOn:TRUE animated:YES];
}
}
Regards.
I think the error is the following:
#interface SettingsViewController : UIViewController <UINavigationControllerDelegate>
{
UISwitch *gravaSwitch;
...
}
#property (retain, nonatomic) IBOutlet UISwitch *gravaSwitch;
...
#end
IBOutlet placeholder has to be inserted into #property. Change your code and try to connect your outlet again.
Edit:
Try to create your UISwitch programatically. Change your #property as the following:
#property (retain, nonatomic) UISwitch *gravaSwitch;
Leave #synthesize as is. Then in your viewDidLoad method add your UISwitch (by default on property is FALSE):
// Width and height are set to zero but they take the default dimension
UISwitch *yourSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(100, 100, 0, 0)];
// add target and action
mySwitch addTarget:self action:#selector(yourCustomAction:) forControlEvents:UIControlEventValueChanged];
self.gravaSwitch = yourSwitch;
// don't forget because gravaSwitch has already a retain policy
[release yourSwitch];
// add the switch to the view
[self.view addSubview:self.gravaSwitch];
Within the same controller, but outside the viewDidLoad method, add the following method:
- (void)yourCustomAction:(id)sender
{
if ( self.gravaSwitch.on )
{
NSLog(#"IF");
[self.gravaSwitch setOn:FALSE animated:YES];
}
else
{
NSLog(#"ELSE");
[self.gravaSwitch setOn:TRUE animated:YES];
}
}
- (void)dealloc
{
self.gravaSwitch = nil; // remember to dealloc the switch!!
[super dealloc]
}
You could call self.gravaSwitch = nil; also in viewDidUnload method.
As an alternative you can set gravaSwicth to assign policy as follow. In this case you haven't to call self.gravaSwitch = nil; both in dealloc and/or viewDidUnload.
#property (assign, nonatomic) UISwitch *gravaSwitch;
Hope it helps.
Edit 2:
This code works for me. This is the implementation (.m file) for MyViewController.
#synthesize gravaSwitch;
- (void)viewDidLoad
{
[super viewDidLoad];
UISwitch *yourSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(100, 100, 0, 0)];
[yourSwitch addTarget:self action:#selector(yourCustomAction:) forControlEvents:UIControlEventValueChanged];
self.gravaSwitch = yourSwitch;
[yourSwitch release];
[self.view addSubview:self.gravaSwitch];
}
- (void)yourCustomAction:(id)sender
{
if(self.gravaSwitch.on)
{
NSLog(#"on");
}
else
{
NSLog(#"off");
}
}
where gravaSwicth is declared within MyViewController.h as follows:
#interface MyViewController : UIViewController
{
UISwitch *gravaSwitch;
...
}
#property (retain, nonatomic) UISwitch *gravaSwitch;
...
#end
rembember to call self.gravaSwicth = nil in dealloc in MyViewController.m!!
FWIW I had this problem when my UISwitch was in a UITableViewCell, and I put it at the top of the list of subviews in Interface Builder (and therefore beneath the other views in the cell when running). It worked in a develop build but not a production build for who knows what reason. In prod, I would click on the UISwitch and the whole row would blink ask if selected.
So I moved it to the bottom of the list (and therefore above the other views in the cell when running) and then I could click on the UISwitch.
-(IBAction)alarmSwitchToggled:(id)sender
{
if ([switchAlarm isOn]) {
NSLog(#"switch is ON");
}
else
{
NSLog(#"switch is OFF");
}
}
Give proper connections in nib file and dont forget to make it as valuechanged instead of touchupinside