I'm stuck on a concept in iOS that I can't seem to understand, no matter how much I read about it. I'm trying to override the standard iOS number pad with a custom design. When the user touches the UITextField, I want the custom inputView to reveal instead of the standard number pad.
I created an separate .h/.m/.xib ViewController class for my custom inputView called "customInputViewController" Right now, it's just a dark background and one button that obscures about half of the screen when the UITextField is touched (similar to the number pad, but it just looks different). My implementation fails when I click the one button in my custom inputView -- iOS throws an EXC_BAD_ACCESS error.
This is how I load the .xib file at runtime and attach the custom inputView to the UITextField object:
UIViewController *v = [[customInputViewController alloc] initWithNibName:#"customInputDesign" bundle:nil];
myTextInput.inputView = v.view;
In the .xib file of the custom inputView, I set the File's Owner to be "customInputViewController" and I created an (IBAction) method and attached it to a UIButton. When that button is clicked, the (IBAction) is set up to send an NSLog(#"Button Clicked") message. Nothing special. It's just a simple boilerplate implementation that continues to throw an error.
Maybe I'm doing this entirely wrong. Can anyone provide a simple example?
The view v.view is retained as the inputView property is defined as (readwrite, retain). However, if you release your customInputViewController v somewhere before the input button is clicked, you will get a crash (EXC_BAD_ACCESS)
You can try this in your main controller:
- (IBAction) keyboardButtonClicked
{
NSLog(#"keyboard Button Clicked");
}
- (void) viewDidLoad
{
// do your stuff here ...
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; // add autorelease if you don't use ARC
v.backgroundColor = [UIColor darkGrayColor];
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setTitle:#"Test button" forState:UIControlStateNormal];
[b addTarget:self action:#selector(keyboardButtonClicked) forControlEvents:UIControlEventTouchUpInside];
b.frame = CGRectMake(80, 25, 160, 50);
[v addSubview:b];
myTextInput.inputView = v;
}
Should work fine ...
First of all, Take a look at this
The UIKit framework includes support for custom input views and input
accessory views. Your application can substitute its own input view
for the system keyboard when users edit text or other forms of data in
a view. For example, an application could use a custom input view to
enter characters from a runic alphabet. You may also attach an input
accessory view to the system keyboard or to a custom input view; this
accessory view runs along the top of the main input view and can
contain, for example, controls that affect the text in some way or
labels that display some information about the text.
To get this feature if your application is using UITextView and
UITextField objects for text editing, simply assign custom views to
the inputView and inputAccessoryView properties. Those custom views
are shown when the text object becomes first responder...
Actually i don't need to mention all this mess to you, but there is an interesting reason for mentioning this, from the first sentence i am mentioning view-view-view, but you are making the input view in a separate view controller and you are trying to assign it as an input view of your textfield and init shouldn't be creating the view, loadView does that. Calling the view getter (v.view) when view is nil will cause loadView to be invoked.Thats why it is crashing with EXC_BAD_ACCESS.
Source : Text, Web, and Editing Programming Guide for iOS
Related
Created a UIButton programmatically but the page it needs to link to will need to be the root controller view of a UINavigationController.
This page would be easier to create in interface builder rather than in code. When the button in question is tapped it needs to segue to another controller/view that I can edit in interface builder.
Possible or impossible?
If possible how can I do this? I feel I'll run into this problem quite often.
You can do this :
Button creation:
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[btn addTarget:self action:#selector(btnMethod:) forControlEvents:UIControlEventTouchUpInside];
Button method:
- (void)btnMethod:(UIButton *)sender
{
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:sender];
}
Sure it's possible:
Create the button:
UIButton *bottonOne = [[UIButton alloc]initWithFrame:CGRectMake(10, 15, 65, 12)];
The set it up with a title / image as you need. Once you have done that do this:
[buttonOne addTarget:self action:#selector(buttonOnepressed) forControlEvents:UIControlEventTouchUpInside];
Then in the same class create a method called buttonOnepressed
-(void)buttonOnePressed
{
//preform the steps needed for your segue method and anything else you want to do when the button has been tapped
}
Edit
After reading your comments - you want a button you've created in code, to perform a segue without manually invoking the segue (Writing code to actually show the new screen) and you want it to behave like it would if you did a drag and drop in IB. If that's the case - the short answer is simply, no. If you create a button in code, all its actions need to be done in code, too.
Edit 2
Try this:
In IB create a "generic" segue like this:
Ctrl-drag from the source view controller to the other view you want to do to when the button is tapped. You can use the view controller object at the bottom of the scene to do this.
Give the segue an identifier.
Then use [self performSegueWithIdentifier#"Your Identifier"]; in your button tapped method to perform the segue
I am currently developing a prototype that I want to do user testing on desktop first before loading to iPad.
I am looking for solutions to disable the keyboard after clicking a textfield. That means after clicking a textfield, user is able to enter information from the macbook keyboard directly, and the virtual keyboard that automatically shows up in the simulator will not appear. I have been through a lot of tutorials but they are all dismissing the keyboard after user entry; that is not what I am looking for. How should I hide the keyboard?
Thanks so much!
Use this:
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
myTextField.inputView = dummyView; // Hide keyboard, but show blinking cursor
It works for UITextField and UITextView and they need to be set to editable.
What you did Here:
You created a dummy view of width=hight=0, & assigned it as the inputView of your textField.
How It works:
Instead of showing default, keyboard, now, the viewController is showing DummyView as inputView for your UITextField. As DummyView has Width=height=0, You will not see anything on the screen :)
Here is another answer which I found the same hack but with little additional supportive code snippet to hide the blinking cursor too.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
return NO; // Hides both keyboard and blinking cursor.
}
I needed this to be done for a Quantity text field where I increase/decrease the quantity using a UIStepper view. So I needed the keyboard to be hidden always.
This will set the inputView of your textField to, basically, an empty UIView with no frame.
self.theTextField.inputView = [[UIView alloc] initWithFrame:CGRectZero];
I'm trying to achieve a similar keyboard interaction that Messages has in iOS 7. I have a UIView which contains a UITextView, and when the user selects it to start typing, I want to make this UIView the inputAccessoryView. This would take care of the animation for me, as well as the new UIScrollView keyboard dismiss interaction in iOS 7.
When the UITextView begins editing, I'm trying to set its inputAccessoryView to its parent UIView (which is already in the view hierarchy). The keyboard appears but not with an accessory view.
I've read some people are using a duo of UITextFields to make this work, but that seems like a bad way to achieve this.
Any suggestions?
A much easier solution is to make your input field the input accessory view of your view controller:
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (UIView *)inputAccessoryView
{
return self.yourInputField;
}
The view will be on screen at the bottom of the screen and when it becomes first responder in response to a user tapping it, the keyboard will be presented. The view will be animated such that it remains immediately above the keyboard.
The only way to get this to work is via a second text field. The idea is to make it a subview but not visible (due to crazy rect). You then switch firstResponder back and forth between it and the real text field while its getting delegate methods. I created a some one viewController test project and did this (you can copy paste and verify behavior with about 2 minutes of time):
#implementation ViewController
{
UITextField *field;
UITextField *dummyView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
field = [[UITextField alloc] initWithFrame:CGRectMake(0, 460, 320, 20)];
field.borderStyle = UITextBorderStyleRoundedRect;
field.delegate = self;
//field.inputAccessoryView = field;
field.text = #"FOO";
[self.view addSubview:field];
dummyView = [[UITextField alloc] initWithFrame:CGRectMake(0, 40000, 320, 20)];
dummyView.delegate = self;
[self.view addSubview:dummyView];
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if(textField == field && textField.superview == self.view) {
[field removeFromSuperview];
dummyView.inputAccessoryView = field;
[dummyView becomeFirstResponder];
}
return YES;
}
#end
I should add I've used this technique in shipping apps since iOS 4.
EDIT: So a couple of other ideas:
1) To make the glitch when the keyboard starts moving look a little better, you could take a snapshot of your textView, put that into a UIImageView, and when you remove the textView from the primary view, replace it with the UIImageView. Now the appearance is the same. Add an animation for the image so that noting happens for 50 ms, then the alpha goes to 0. Add a similar animation to your real textview, so that it has an alpha of 0 for 50 ms, then it goes to 1. You may be able to tweak this so the transition is good (but not great).
2) The way apple probably does this is to get the animation curve and timing from the keyboard moving notification. In this case they would add a accessory view with 0 height at first, and animate the textField so its tracking the keyboard, but above it. Both moving same distance at the same time. At the end of the animation, the textField is pulled out of self.view, the accessory view has its frame changed to have the height of the textField, and the textField is placed as a subview of the accessory container view. This should work but yeah, its a bit complex to do. If you want someone to code it for you offer a 100 pt bounty. You still need the dummy text field for when you go and move the textField at the end, since when you take it out of its containing view it will resign first responder. So at the end, you make the dummy field the first responder, move the textfield, then make the real textfield the first responder again.
This actually works best if you don't use .inputAccessoryView at all and instead just animate the position of the parent UIView as the keyboard opens and closes. Here is an answer describing the process step-by-step with all the code.
I have Storyboard in my project. I use [UIBarButtonItem setCustomView:] method to customize toolbar buttons. For example:
[self setCustomView:[[UIImageView alloc] initWithImage:image]];
Now they looks as I need. But I found that this method somehow disables segue that I set for this toolbar item. I mean, segue works without it, but when I tried to use this method for customization segue don't work. But why?
I don't want to use target-action pattern from code, I believe it is possible to use only Storyboard.
I've been trying to configure all of the segues in the storyboard as well but I needed to create a custom view for the rightBarButtonItem. To make sure the segue still works, just add this line before setting the customView:
[filterButton addTarget:self.navigationItem.rightBarButtonItem.target action:self.navigationItem.rightBarButtonItem.action forControlEvents:UIControlEventTouchUpInside];
This way the segues you set up in your storyboard will still fire with customViews.
My entire custom button code looks like this:
UIButton *filterButton = [UIButton buttonWithType:UIButtonTypeCustom];
[filterButton setImage:[UIImage imageNamed:#"filter"] forState:UIControlStateNormal];
[filterButton setFrame:CGRectMake(0, 0, 36, 36)];
[filterButton addTarget:self.navigationItem.rightBarButtonItem.target action:self.navigationItem.rightBarButtonItem.action forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem.customView = filterButton;
I've tried what you have done with the same result - the picture is correct but the UIBarButtonItem doesn't react when pressed. Possibly this is a bug. My work around is as follows:
To do this in storyboard add a UIButton to the tool bar. You should see that storyboard adds it by putting the UIButton inside of the UIBarButton. Add the segue on the UIButton. Customize the UIButton in storyboard. In my App I set the size to 40 x 40. Then in your code customize the UIButton with the view. Here is an example of the code to add the imageView to the UIButton:
[sampleImageView setFrame:CGRectMake(0, 0, 40, 40)];
[swapButton addSubview:sampleImageView];
Note: Storyboard can be quirky about adding UIButtons to toolbars. It seems as if you do it in a toolbar that is tied to a navigation controller it won't let you add it. I've worked around this by adding a dummy view controller to the storyboard, adding a toolbar to that, then dragging the UIButton into that toolbar. Storyboard will create that for you by encapsulating the UIButton in a UIButtonBarItem. You can then copy then over to the the desired toolbar in your project and delete dummy view controller.
There are other ways to do this such as creating the buttons in code and adding them to the toolbar. The method above minimizes code.
I don't know what is wrong with this code; when i start typing in the UITextView, the program terminates with exc_Bad_Access exception.
UIView *toolbar = [[UIView alloc] initWithFrame:CGRectMake(0, 430, 320, 44)];
toolbar.backgroundColor = [UIColor lightGrayColor];
UITextView *sendTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 9, 240, 26)];
sendTextView.backgroundColor = [UIColor whiteColor];
sendTextView.inputAccessoryView = toolbar;
sendTextView.layer.cornerRadius = 12.0;
[toolbar addSubview:sendTextView];
[self.view addSubview:toolbar];
The above code is inside the viewDidLoad method of a UIViewController which has a UIScrollView as its view.
Putting an editable text view in a toolbar seems strange. (What do you do when the user wants to edit it? Move it up above the keybaord? I wouldn't expect a toolbar to move OR to contain an editable field.) Nevertheless, I'd be surprised if doing that caused EXC_BAD_ACCESS.
Your problem is more likely in code that actually runs when you're typing, such as one of the text view delegate methods. If you can't find anything there, please post the stack trace at the time of the crash and code for the method that was actually running at the time.
Update: After you pointed it out in your comment, I see that you're setting the toolbar as the input accessory view for sendTextView and adding it to the view controller's view. I'd guess that what's happening here is that when you start editing the text view, the text view adds the toolbar to the keyboard's view without first removing it from the view controller's view. A given view can only be part of one view hierarchy at a time; adding it to your view and using it as the input accessory view won't work. If you look at Apple's sample code for using an accessory view you'll find that the view used as the accessory isn't part of the normal view hierarchy.