How to disable alertview's button in iPhone? - iphone

I have alert view having 2 buttons "OK" and "Cancel" and a textfield.
Now i want to disable "OK" button until user enter some text in textfield.
How can i do this?
thanks in advance

UPDATE 2: For Swift 5.1
<#your alert controller#>.addTextField {(tf) in
//... set your tf characteristics i.e .keyboardType here
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
object: tf,
queue: OperationQueue.main) { _ in
//enable or disable the selected action depending on whether the textField text is empty
<#your alert controller#>.actions[0].isEnabled = !tf.text!.isEmpty
}
}
posting this to update the response since ios 5 :
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
UITextField *textField = [alertView textFieldAtIndex:0];
if ([textField.text length] == 0)
{
return NO;
}
return YES;
}
UPDATE:iOS 8 Since Apple have deprecated the UIAlertView in favour of the UIAlertController. There is no longer a delegate call to alertViewShouldEnableFirstOtherButton:
So instead you would set the buttons enabled property via the UITextFieldTextDidChangeNotification
Add a textView to the alert with
(void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler
[<#your alert#> addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.delegate = self;
textField.tag = 0; //set a tag to 0 though better to use a #define
}];
Then implement the delegate method
(void)textFieldDidBeginEditing:(UITextField *)textField
- (void)textFieldDidBeginEditing:(UITextField *)textField{
//in here we want to listen for the "UITextFieldTextDidChangeNotification"
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldHasText:)
name:UITextFieldTextDidChangeNotification
object:textField];
}
When the text in textField changes it will invoke a call to "textFieldHasText:" and pass along a NSNotification*
-(void)textFieldHasText:(NSNotification*)notification{
//inside the notification is the object property which is the textField
//we cast the object to a UITextField*
if([[(UITextField*)notification.object text] length] == 0){
//The UIAlertController has actions which are its buttons.
//You can get all the actions "buttons" from the `actions` array
//we have just one so its at index 0
[<#your alert#>.actions[0] setEnabled:NO];
}
else{
[<#your alert#>.actions[0] setEnabled:YES];
}
}
Don't forget to remove your observer when done

I wanted to extend the answer by Ryan Forsyth by adding this. If you add a Default styled UIAlertView, you can get an out of range exception if you try to access a textfield as none exist, so check your view style first.
-(BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView*)alertView
{
if(alertView.alertViewStyle == UIAlertViewStyleLoginAndPasswordInput ||
alertView.alertViewStyle == UIAlertViewStylePlainTextInput ||
alertView.alertViewStyle == UIAlertViewStyleSecureTextInput)
{
NSString* text = [[alertView textFieldAtIndex:0] text];
return ([text length] > 0);
}
else if (alertView.alertViewStyle == UIAlertViewStyleDefault)
return true;
else
return false;
}

You can create two buttons for Ok and Cancel.Then add those two as sub views in the UIAlertView.Now by checking the text(text length) in the textfield,You can perform enable and disable actions.

Without knowing the context of your application, the following may not apply - but have you read the iOS Human Interface Guidelines? It sounds as though you may be better off finding an alternative to UIAlertView if this is something that's going to be displayed to the user often.

Not really related to your question, but do not modify default UIAlertView if you don't want your app to be rejected. If I'm not wrong, you're adding textfields to the alertview, don't you? Like a login view. You should create your own view.
So, regarding your question, create your view, set the buttons has disabled, and delegate the UITextFields. When
- (void)textFieldDidBeginEditing:(UITextField *)textField;
is called, enable those buttons.

Related

Add Power Button to Calculator App

I am trying to create a "Power Button" for my calculator app that will turn the calculator on/off. I created the button:
-(IBAction) *Power;
Without the power button, the app starts with "0" in the LCD display, and my buttons are all working; but how can I tell my app that the LCD should display "" and the buttons cannot be manipulated until the power button is pressed? Would a simple if-then function work? Can I put -(IBAction) buttons within if-thens?
Create a BOOL property, that and check for YES in all the IBACtions.
You need to set it to YES/NO on the method Power, typically as:
Initiaze it with NO in your init or viewDidLoad, whichever is applicable.
-(IBAction) power{
self.powerOn=!self.powerOn;
}
-(IBAction) otherMethod{
if(self.powerOn){
//do your stuff
}
}
Also, method with pointer -(IBAction) *Power; is not that you need here.
1) declare a variable to track power on/off
#property(nonatomic, assign) BOOL powerOn;
in viewDidLoad,
assign _powerOn = NO; or _powerOn = YES;
2) inside your power button pressed event,
-(IBAction) Power;{
//if on, then off
if(self.powerOn){
//make display ""
self.powerOn = NO;
}else{ //if off, the on
self.powerOn = YES;
//make display "0"
}
}
3) add following line as the 1st line in all other button click events
-(IBAction) otherMethod{
if(!self.powerOn) return;
//your code
}
You Need to create IBOutlets for your Buttons, to edit behaviors of them while running the app.
These IBOutlets are created like this:
IBOutlet UIButton *myPowerButton;
You need to link them with the button in Interface Builder where you created the Outlet for.
There you can specify the 'Enabled' behavior to be YES or NO, so you can make a Power Button to set if the user can work or not.
For further information please read Apples Documentation about IBOutlets and Buttons.
UIButton Apple Documentation
I would do this way, assuming the title of the button is Power:
-(IBAction) Power
{
if(self.powerOn)
{
//make display ""
self.powerOn = NO;
for (UIView* subView in self.view.subviews)
{
if ([subView isMemberOfClass:[UIButton class]] && subView.titleLabel.text != #"Power")
subView.userInteractionEnabled = NO;
}
}
else
{ //if off, the on
self.powerOn = YES;
//make display "0"
}
}
I would disable the User interaction of all other button when powerbutton is off.

Will disabling UIAlertView buttons get my app rejected?

I'm using an AlertView with a UITextView subview to let users reply to posts in my app, but I want the Reply button of the alert to disable when the user types more than the character limit. Will disabling the alert view button like this get my app rejected, is there a better way to do this?
-(void)textViewDidChange:(UITextView *)textView {
if (!replyAlert) {
return;
}
//character count
replyAlert.title = [NSString stringWithFormat:#"Reply to Post (%i/250)", [textView.text length]];
if ([textView.text length]>=250) {
//disable alert view button
for (UIView* view in [replyAlert subviews])
{
if ([[[view class] description] isEqualToString:#"UIAlertButton"])
{
UIButton *button = (UIButton*)view;
if ([button.titleLabel.text isEqualToString:#"Reply"]) {
//disable
button.enabled = NO;
}
}
}
} else if ([textView.text length]==249) {
//re-enable button if user deleted a character
for (UIView* view in [replyAlert subviews])
{
if ([[[view class] description] isEqualToString:#"UIAlertButton"])
{
UIButton *button = (UIButton*)view;
if ([button.titleLabel.text isEqualToString:#"Reply"]) {
//enable
button.enabled = YES;
}
}
}
}
}
Have a look at this method on the delegate (UIAlertViewDelegate)
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
This method will be called each time a user types a character into a text field in the alert view, assuming you are using the UIAlertViewStylePlainTextInput (?). So in this method you could check the length of the text in the text field and return TRUE/FALSE accordingly.
The method is only available in iOS 5.0 or later too which may be an issue if supporting older versions.
If you are adding your own text fields as subviews to the alert view, then this alone is cause for the app to be rejected as it states that the view hierarchy is not to be manipulated. If you are using the text input style alert view out-of-the-box and just navigating the subviews to check the button titles and disable them, I'd be surprised (note this is a subjective opinion) if that caused a rejection of the app.

How to set cursor position for UITextView on user input?

I am looking for a simple answer for this problem...
I have a UITextView in which the user can start typing and click on DONE and resign the keyboard.
When the wants to edit it again, I want the cursor (the blinking line) to be at the first position of the textView, not at the end of textView. (act like a placeholder)
I tried setSelectedRange with NSMakeRange(0,0) on textViewDidBeginEditing, but it does not work.
More Info:
It can be seen that.. when the user taps on the textView the cursor comes up at the position where the user taps on the textView.
I want it to always blink at starting position when textViewDidBeginEditing.
The property selectedRange can not be assigned at "any place", to make it work you have to implement the method - (void)textViewDidChangeSelection:(UITextView *)textView, in your case:
- (void)textViewDidChangeSelection:(UITextView *)textView
{
[textView setSelectedRange:NSMakeRange(0, 0)];
}
you will have to detect when the user is beginning editing or selecting text
My solution:
- (void) viewDidLoad {
UITextView *textView = [[UITextView alloc] initWithFrame: CGRectMake(0, 0, 200, 200)];
textView.text = #"This is a test";
[self.view addSubview: textView];
textView.delegate = self;
[textView release];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(tapped:)];
[textView addGestureRecognizer: tap];
[tap release];
}
- (void) tapped: (UITapGestureRecognizer *) tap {
[textView becomeFirstResponder];
}
- (void) textViewDidBeginEditing:(UITextView *)textView {
textView.selectedRange = NSMakeRange(0, 0);
}
I guess it's UITextView internal mechanism to set the cursor when user taps on it. We need to override that by attaching a tap gesture recognizer and call becomeFirstResponder instead.
I was facing the same issue - basically there's a delay when becoming first responder that doesn't allow you to change selectedRange in any of textView*BeginEditing: methods. If you try to delay the setSelectedRange: (let's say with performSelector:withObject:afterDelay:) it shows ugly jerk.
The solution is actually pretty simple - checking order of delegate methods gives you the hint:
textViewShouldBeginEditing:
textViewDidBeginEditing:
textViewDidChangeSelection:
Setting selectedRange in the last method (3) does the trick, you just need to make sure you reposition the cursor only for the first time when the UITextView becomes first responder as the method (3) is called every time you update the content.
A BOOL variable set in shouldChangeTextInRange: one of the methods (1), (2) and check for the variable in (3) should do the trick ... just don't forget to reset the variable after the reposition to avoid constant cursor reset :).
Hope it helps!
EDIT
After few rounds of testing I decided to set the BOOL flag in shouldChangeTextInRange: instead of (2) or (3) as it proved to be more versatile. See my code:
#interface MyClass
{
/** A flag to determine whether caret should be positioned (YES - don't position caret; NO - move caret to beginning). */
BOOL _isContentGenerated;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// deleting
if([text length] == 0)
{
// deleting last character
if(range.length == [[textView text] length])
{
// reached beginning
/**
code to show placeholder and reset caret to the beginning
*/
_isContentGenerated = NO;
}
}
else
{
// adding
if(range.location == 0)
{
/**
code to hide placeholder
*/
_isContentGenerated = YES;
}
}
return YES;
}
- (void)textViewDidChangeSelection:(UITextView *)textView
{
if(!_isContentGenerated)
{
[textView setSelectedRange:NSMakeRange(0, 0)];
}
}
I haven't worked enough with that to help you fully, but what happens when you try to play with different selectedRanges? Say, if you do [... setSelectedRange:[NSMakeRange(0,1)]] or [... setSelectedRange:[NSMakeRange(1,0)]]? Does it move the cursor anywhere?
So I ended up adding a UILabel over the UITextView which acts as a placeholder for the textView. Tapping on the UILabel would send the action down to the textView and becomeFirstResponder. Once you start typing, make the label hidden.
[_detailAreaView setTextContainerInset:UIEdgeInsetsMake(8, 11, 8, 11)];

Is there a way to prevent the keyboard from dismissing?

I realize that this is the inverse of most posts, but I would like for the keyboard to remain up even if the 'keyboard down' button is pressed.
Specifically, I have a view with two UITextFields. With the following delegate method
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
return NO;
}
I am able to keep the keyboard up even if the user presses the Done button on the keyboard or taps anywhere else on the screen EXCEPT for that pesky keyboard down button on the bottom right of the keyboard.
I am using this view like a modal view (though the view is associated with a ViewController that gets pushed in a UINavigationController), so it really works best from a user perspective to keep the keyboard up all of the time. If anyone knows how to achieve this, please let me know! Thanks!
UPDATE Still no solution! When Done is pressed, it triggers textFieldShouldReturn, but when the Dismiss button is pressed, it triggers textFieldDidEndEditing. I cannot block the textField from ending editing or it never goes away. Somehow, I really want to have a method that detects the Dismiss button and ignores it. If you know a way, please enlighten me!
There IS a way to do this. Because UIKeyboard subclasses UIWindow, the only thing big enough to get in UIKeyboard's way is another UIWindow.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(coverKey) name:UIKeyboardDidShowNotification object:nil];
[super viewDidLoad];
}
- (void)coverKey {
CGRect r = [[UIScreen mainScreen] bounds];
UIWindow *myWindow = [[UIWindow alloc] initWithFrame:CGRectMake(r.size.width - 50 , r.size.height - 50, 50, 50)];
[myWindow setBackgroundColor:[UIColor clearColor]];
[super.view addSubview:myWindow];
[myWindow makeKeyAndVisible];
}
This works on iPhone apps. Haven't tried it with iPad. You may need to adjust the size of myWindow. Also, I didn't do any mem management on myWindow. So, consider doing that, too.
I think I've found a good solution.
Add a BOOL as instance variable, let's call it shouldBeginCalledBeforeHand
Then implement the following methods:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = YES;
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
return shouldBeginCalledBeforeHand;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = NO;
}
As well as
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return NO;
}
to prevent the keyboard from disappearing with the return button. The trick is, a focus switch from one textfield to another will trigger a textFieldShouldBeginEditing beforehand. If the dismiss keyboard button is pressed this doesn't happen. The flag is reset after a textfield has gotten focus.
Old not perfect solution
I can only think of a not perfect solution. Listen for the notification UIKeyboardDidHideNotification and make of the textfields first responder again. This will move the keyboard out of sight and back again. You could keep record of which textfield was the last firstResponder by listening for UIKeyboardWillHideNotification and put focus on it in the didHide.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
...
- (void)keyboardDidHide:(id)sender
{
[myTextField becomeFirstResponder];
}
For iOS 9/10 and Swift 3, use this to create a rect which overlaps the "Hide keyboard" - Button
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(coverKey), name: .UIKeyboardDidShow, object: nil)
}
func coverKey() {
if let keyboardWindow = UIApplication.shared.windows.last {
let r = UIScreen.main.bounds
let myWindow = UIWindow.init(frame: CGRect(x: r.size.width - 50 , y: r.size.height - 50, width: 50, height: 50))
myWindow.backgroundColor = UIColor.clear
myWindow.isHidden = false
keyboardWindow.addSubview(myWindow)
keyboardWindow.bringSubview(toFront: myWindow)
}
}
Notice that this adds a sub view to the keyboard window instead of the main window
Try adding a custom on top of the keyboard dismiss button so that the user won't be able to tab the dismiss button. I have used this method in one of my application.
- (void)addButtonToKeyboard {
// create custom button
UIButton *blockButton = [UIButton buttonWithType:UIButtonTypeCustom];
blockButton.frame = //set the frame here, I don't remember the exact frame
[blockButton setImage:[UIImage imageNamed:#"block_button.png"] forState:UIControlStateNormal];
// locate keyboard view
UIWindow *appWindows = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView *keyboard;
for (int i=0; i<[appWindows.subviews count]; i++) {
keyboard = [appWindows.subviews objectAtIndex:i];
// keyboard found, add the button
if ([[keyboard description] hasPrefix:#"<UIPeripheralHost"] == YES && [self.textField isFirstResponder]) {
[keyboard addSubview:doneButton];
}
}
}
Try this...
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
return NO;
}
You can use notification as mentioned by Nick Weaver.

becomeFirstResponder slows down app

I have two textfields for username and password and a submit button. When the submit button is pressed a check is performed to see if the username and password was typed or not. If not it shows an alert message and the field whose value was not entered becomes the first responder.
-(IBAction)loginPressed:(id)sender {
if ([username.text length] == 0)
{
[self showAlert:#"Invalid Username/ Password"];
[username becomeFirstResponder];
return;
}
if ([password.text length] == 0)
{
[self showAlert:#"Invalid Username/ Password"];
[password becomeFirstResponder];
return;
}
}
I observed that on clicking the button, the button remains selected for about 1.5 seconds and then the alert is shown. If I comment out the becomeFirstResponder method, it works without any pause. However I need becomeFirstResponder to be there. How do I speed things up using this?
Switch the ordering of becomeFirstResponder and showAlert.
[self showAlert:#"Invalid Username/ Password"]; will take a time. you cant speeup that thing.