This happens only on device and not on simulator..
I have two custom views and both have a UITextField in them(Child1 and Child2).
Both these views are placed on another UIView (Say viewA).
Now my requirement is such that when text is entered in one of the textfield I need to clear the other text fields content, so in the textFieldDidChange: method I inform viewA and than it iterates over its subview finds Child1 and sets its properties. But as soon as I access the textField of this Child1 to enable its userInteraction or and set its text to nil. This textfield now becomes the first responder.
I am not really sure why it does that. You can look at the below code to get more info.
Method inside viewA:
for (UIView *view in [self subviews])
{
if ([view isKindOfClass:[ChildView class]])
{
ChildView *concernedCustomerView = (ChildView *)view;
if (concernedCustomerView.typeOfCompartment == CompartmentTypeNone)
{
[concernedCustomerView.checkBoxButton setSelected:NO];
[concernedCustomerView.countSwitch setOn:NO animated:YES];
concernedCustomerView.countTextField.userInteractionEnabled = YES;
concernedCustomerView.countTextField.alpha = 1.0f;
concernedCustomerView.countTextField.text = nil;
}
}
}
Method inside custom Child View
-(void)textFieldDidChange:(id)sender
{
NSString *note = _countTextField.text;
note = [note stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//If we are checking note for nil it should be before calling trimming white space
if (note && note.length > 0)
{
[_checkBoxButton setSelected:YES];
if (note.length == 3 && note.integerValue == 999)
{
[_countSwitch setOn:YES animated:YES];
_countTextField.userInteractionEnabled = NO;
_countTextField.alpha = 0.5f;
_countTextField.text = nil;
// [_countSwitch sendActionsForControlEvents:UIControlEventValueChanged];
}
}
else
{
[_checkBoxButton setSelected:NO];
}
if ([self.delegate conformsToProtocol:#protocol(ChildViewDelegate)] &&
[self.delegate respondsToSelector:#selector(adjustStateOfOtherControls:andCheckBoxStatus:)])
{
[self.delegate adjustStateOfOtherControls:_typeOfCompartment andCheckBoxStatus:_checkBoxButton.selected];
}
}
Do not set the view object to nil because they are alive and your controller is still active,
try to set _countTextField.text = #""; so that your textfield become empty.
Just a suggest:
1) Instead of manual iterate subviews you can assign tag to child views and uitextfields e.g:
child1View.tag = 100;
child2View.tag = 200;
...
textField1.tag = 10;
textField2.tag = 20;
then get child references from parent viewA by:
UIView *child1View = [viewA viewWithTag:100];
UIView *child2View = [viewA viewWithTag:200];
2) Set child views textfield delegate to a common viewcontroller
3) Handle one single
- (void)textFieldDidBeginEditing:(UITextField *)textField
4) Iniside this method check
if(textField.tag==10)
{
do stuff
}
else if(textfield.tag==20)
{
do other stuff
}
Hope it helps !
Related
I have a text field that I want to have a keyboard like this when user start typing:
please also see this video: https://youtu.be/iU_jocny3N0
As you can see in this video there is a "ABC" key that helps user to switch from number pad to text. and also when press "123" in text the keyboard switchs from text to number pad. I am wondering how they do this?
The only solution that I found was adding a subview to keyboard like what described here:
Adding Done Button to Only Number Pad Keyboard on iPhone
but this way may not work when user uses custom keyboards. and also do not works for switching from text to number pad.
Or as another solution I know accessoryInputView but this is not like the video. It adds a toolbar above the keyboard.
Does someone knows the solutions that is used in this video?
I have added comma button to the keyboard,
Keyboard is also a simple UIView Which contains Controls
NOTE: This is old code was working in my old project Not tested in new projects
- (void) keyboardWillShow:(NSNotification *)note {
// create custom button
dispatch_async(dispatch_get_main_queue(), ^{
// Some code
UITextField *txt = (UITextField *)[self.view findFirstResponder];
if (txt.keyboardType == UIKeyboardTypeDecimalPad) {
UIButton * btnComma = [UIButton buttonWithType:UIButtonTypeCustom];
[btnComma setTag:15000];
UIView* keyboard = [self findKeyboard];
// btnComma.frame = CGRectMake(0, 162, 126, 54);
btnComma.frame = [self findKeySizeForView:keyboard];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[btnComma setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 20, 0)];
}
[btnComma setBackgroundColor:[UIColor colorWithHexString:#"CBD0D6"]];
btnComma.adjustsImageWhenHighlighted = NO;
[btnComma setTitle:#"." forState:UIControlStateNormal];
[btnComma setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnComma.titleLabel setFont:[UIFont systemFontOfSize:35.0f]];
[btnComma addTarget:self action:#selector(commaBtnTapped) forControlEvents:UIControlEventTouchUpInside];
[keyboard addSubview:btnComma];
btnComma = nil;
}
});
}
- (UIView *) viewWithPrefix:(NSString *)prefix inView:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([[subview description] hasPrefix:prefix]) {
return subview;
}
}
return nil;
}
This method for finding keyboard from UIWindow
- (UIView *) findKeyboard {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
UIView *inputSetContainer = [self viewWithPrefix:#"<UIInputSetContainerView" inView:window];
if (inputSetContainer) {
UIView *inputSetHost = [self viewWithPrefix:#"<UIInputSetHostView" inView:inputSetContainer];
if (inputSetHost) {
UIView *kbinputbackdrop = [self viewWithPrefix:#"<_UIKBCompatInput" inView:inputSetHost];
if (kbinputbackdrop) {
UIView *theKeyboard = [self viewWithPrefix:#"<UIKeyboard" inView:kbinputbackdrop];
return theKeyboard;
}
}
}
}
return nil;
}
and For finding size of bottom right button
- (CGRect ) findKeySizeForView:(UIView *)view {
if (view != nil) {
UIView *uiKeyboardImpl = [self viewWithPrefix:#"<UIKeyboardImpl" inView:view];
if (uiKeyboardImpl != nil) {
UIView *uiKeyboardLayoutStar = [self viewWithPrefix:#"<UIKeyboardLayoutStar" inView:uiKeyboardImpl];
if (uiKeyboardLayoutStar != nil) {
UIView *uiKBKeyplaneView = [self viewWithPrefix:#"<UIKBKeyplaneView" inView:uiKeyboardLayoutStar];
if (uiKBKeyplaneView != nil) {
for (view in [uiKBKeyplaneView subviews]) {
CGPoint pointOrigin = view.layer.frame.origin;
if (pointOrigin.x <= 0 && pointOrigin.y == uiKBKeyplaneView.frame.size.height - view.frame.size.height && [[view description] hasPrefix:#"<UIKBKeyView"])
return view.layer.frame;
}
}
}
}
}
return CGRectZero;
}
In my current project I need to maintain multiple container controllers in a single view controller. There are four buttons on view controller. if first button is selected first container will be visible and remaining will be in hidden state. Similarly to second, third and fourth button. At any time only one will be visible to the user. I can achieve it by showing the respective container and hiding rest by hardcoding.
#property UIView *view1;
#property UIView *view2;
#property UIView *view3;
#property UIView *view4;
- (iBAction *)firstButtonClicked:(UIButton *)button {
self.view1.hidden = NO;
self.view2.hidden = YES;
self.view3.hidden = YES;
self.view4.hidden = YES;
}
- (iBAction *)secondButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = NO;
self.view3.hidden = YES;
self.view4.hidden = YES;
}
- (iBAction *)thirdButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = YES;
self.view3.hidden = NO;
self.view4.hidden = YES;
}
- (iBAction *)fourthButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = YES;
self.view3.hidden = YES;
self.view4.hidden = NO;
}
But I am not satisfied with the approach. I tried searching answer in stack overflow but not successful.
Please tell me know if any body knows any effective approach to achieve it.
Thanks.
There are a few possible solutions to this. Here's one option.
First, give each button a specific tag. Give button 1 a tag of 1. Give button 2 a tag of 2, etc.
Then use a single action for all four buttons instead of the four separate actions you have now.
Then implement the one action method like this:
- (IBAction *)buttonClicked:(UIButton *)button {
self.view1.hidden = button.tag != 1;
self.view2.hidden = button.tag != 2;
self.view3.hidden = button.tag != 3;
self.view4.hidden = button.tag != 4;
}
If the button with a tag of 1 is tapped, then button.tag != 1 will be false so self.view1.hidden will be set to NO. The other 3 conditions will be true so the other buttons will have hidden set to YES.
The same logic applies to the other three buttons each with their own tag values.
Give the views tags like 201 to 204 or anything you like, set the for loop accordingly.
Point the actions of all the buttons to the below selector,
- (void)anyButtonClicked:(UIButton *)button
{
for (int iterator = 201; iterator < 204; iterator ++)
{
UIView *currentView = [self.view viewWithTag:iterator];
if (currentView.tag == button.tag)
{
[currentView setHidden:NO];
} else {
[currentView setHidden:YES];
}
}
}
you can also try this it will also consume less memory-
[yourview removeFromSuperview];
yourview = nil;
At any time only one view is hidden. Why do you need to hide/unhide all of them? Just do the following (just after the #property declarations):
UIView *lastVisible = view1;
And then you can write the actions as:
- (IBAction *)firstButtonClicked:(UIButton *)button {
lastVisible.hidden = YES;
self.view1.hidden = NO;
lastVisible = self.view1;
}
and so on. This assumes that view1 is the visible view at first.
I have 2 labels.
First label, named "label" is placed inside every view within the carousel. The string/text of the label is the view's index.
label.text = [[items1 objectAtIndex:index] stringValue];
I also have a second label (outside the carousel) named "outsideLabel".
I want the outsideLabel's string/text to be the view's index aswell (always the view being in front of the carousel).
outsideLabel.text = [[items1 objectAtIndex:index] stringValue];
Somehow I am doing it wrong and wonder how I shall code this in order to show the proper number in outsideLabel's string/text (always the view being in front). The code somewhat shows the correct numbers but get messed up when scrolling backwards in the carousel. The carouseltype is timeMachine.
My current code:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
//create new view if no view is available for recycling
if (view == nil)
{
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
view.contentMode = UIViewContentModeCenter;
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
if (carousel == carousel1)
{
CGRect test = CGRectMake(10, 10, 20, 20);
self.label.frame = test;
}
else {
CGRect test = CGRectMake(50, 40, 40, 40);
self.label.frame = test;
}
[view addSubview:label];
}
else
{
label = [[view subviews] lastObject];
}
if (carousel == carousel1)
{
//items in this array are numbers
outsideLabel.text = [[items1 objectAtIndex:index] stringValue];
label.text = [[items1 objectAtIndex:index] stringValue];
((UIImageView *)view).image = [UIImage imageNamed:[view1background objectAtIndex:index]];
}
else
{
//not relevant....
}
return view;
}
From the code you've provided, it looks like you're not initializing the outsideLabel in the right place. To be safe, you should initialize all your subviews inside the block where you are checking if the view is nil. Another safe convention is to assign tags to all your subviews, so that you can later retrieve them from views that are reused, as in the code below. For easy reference, and to avoid errors, I define constants for these tags at the top of my implementation file, like this:
#define INSIDE_LABEL_TAG 1
#define OUTSIDE_LABEL_TAG 2
This is much safer, since it doesn't depend on the structure of the views, as is the case with your code, where you get the last view:
label = [[view subviews] lastObject];
Try initializing outsideLabel inside that block, and use tags. The pattern used in initialization is identical to that used for the subviews of UITableView cells in a UITableViewDataSource delegate:
(UITableViewCell * _Nonnull)tableView:(UITableView * _Nonnull)tableView
cellForRowAtIndexPath:(NSIndexPath * _Nonnull)indexPath
Here is some pseudo code that shows where I would use tags and initialize the outsideLabel:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
//create new view if no view is available for recycling
if (view == nil)
{
//Configure the view
...
/* Initialize views for all carousels */
//Initialize the insideLabel and set its tag
...
insideLabel.tag = INSIDE_LABEL_TAG;
//Initialize the outsideLabel and set its tag
...
outsideLabel.tag = OUTSIDE_LABEL_TAG;
if (carousel == carousel1)
{
//Do any carousel-specific configurations
}
//Add all subviews initialized in this block
[view addSubview:label];
[view addSubview:outsideLabel];
}
else
{
//Get the subviews from an existing view
insideLabel = (UILabel *)[view viewWithTag:INSIDE_LABEL_TAG];
outsideLabel = (UILabel *)[view viewWithTag:OUTSIDE_LABEL_TAG];
}
if (carousel == carousel1)
{
//Set the values for each subview
} else {
//Other carousels...
}
return view;
}
It looks to me like you want the "time machine" style carousel. I don't see your code setting the carousel type anywhere. Don't you need to set the carousel type?
I have created 8 UIViews, I have a UIPicker and when the user selects something with the UIPicker I run the thread though a big if statement which I am not happy about.
I would like to know if what I am doing is okay? and also if the code I am using to load the view is how it should be done.. the code is below you will see I am simply loading the UIView once, then bringing it to the front layer... I would rather reload it entirely using remove from subview... but I couldn't get that to work..
Any one of the UIViews can be loaded at one time which is what is causing me issue.
#pragma mark - Picker Delegates
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
// ReloadView
if (row == 0) {
if (my1View == nil ) {
[self DrawView1];
}
[self.view bringSubviewToFront:my1View];
} else if (row == 1) {
if (my2View == nil ) {
[self DrawView2];
}
[self.view bringSubviewToFront:my2View];
}else if (row == 2) {
if (my3View == nil ) {
[self DrawView3];
}
[self.view bringSubviewToFront:my3View];
}else if (row == 3) {
if (my4View == nil ) {
[self DrawView4];
}
[self.view bringSubviewToFront:my4View];
}else if (row == 4) {
if (my5View == nil ) {
[self DrawView5];
}
[self.view bringSubviewToFront:my5View];
}else if (row == 5) {
if (my6View == nil ) {
[self DrawView6];
}
[self.view bringSubviewToFront:my6View];
}else if (row == 6) {
if (my7View == nil ) {
[self DrawView7];
}
[self.view bringSubviewToFront:my7View];
}else if (row == 7) {
if (my8View == nil ) {
[self DrawView8];
}
[self.view bringSubviewToFront:my8View];
}
}
The only other issue is that sometimes it will get stuck on one view when no matter what I pick nothing will load over it.
First of all, maybe to make it more beautiful you could think about a switch statement, but in terms of performance this doesn't matter:
switch (row) {
case 0:
if (my1View == nil ) {
[self DrawView1];
}
[self.view bringSubviewToFront:my1View];
break;
case 1:
if (my1View == nil ) {
[self DrawView2];
}
[self.view bringSubviewToFront:my2View];
break;
//THE OTHER CASES HERE...
default:
break;
}
About the rest:
I would suggest you put all the views into an array or dictionary and also have a variable currentView
#property (nonatomic, strong) UIView* currentView;
#property (nonatomic, strong) NSMutableDictionary* myViews;
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
// ReloadView
UIView* myNewView =
[self.myViews objectForKey:[NSString stringWithFormat:#"%d",(int)row]];
if (myNewView!=self.currentView) {
[self.currentView removeFromSuperview];
}
if (!myNewView) {
[self drawView:row];
}
[self.view addSubview:myNewView];
self.currentView = myNewView;
}
Make sure to initiate the myViews property in your viewDidLoad (or something similar) like this:
self.myViews = [NSMutableDisctionary new];
Also you need a new method for this row: [self drawView:row];, in this one you can just call your existing methods or copy your existing methods into this one!
In order to change as little as possible, you can use:
- (void)drawView:(NSInteger)row
{
switch (row) {
case 0:
[self DrawView1];
break;
case 1:
[self DrawView2];
break;
//THE OTHER CASES HERE...
default:
break;
}
}
I have an app that has a place-holder UIView (self.mainView) for viewControllers to be placed based on a tab selection. Using this technique is good because then only 1 of the views is loaded at any time, and I can create a ViewController class that operates with that view instead of having a monolithic controller that needs to understand all the views that may be in that place.
Here is some sample code I use to show the appropriate viewController into that place holder.
#property (weak, nonatomic) IBOutlet UIView *mainView;
#property (strong) UIViewController *currentController;
if (self.currentController) {
[self.currentController willMoveToParentViewController:nil];
[[self.currentController view] removeFromSuperview];
[self.currentController removeFromParentViewController];
self.currentController = nil;
}
switch (row) {
case 0:
self.currentController = [self.storyboard instantiateViewControllerWithIdentifier:#"AboutViewController"];
break;
case 1: {
...
}
break;
}
if (self.currentController) {
[self addChildViewController:self.currentController];
[self.mainView addSubview:self.currentController.view];
[self.currentController didMoveToParentViewController:self];
[self adjustMainView]; // my own method to adjust some constraints base on flags
[self.view layoutIfNeeded];
}
I developer your project and I can't see a problem in your code. Works fine. There is no problem in the code that you are showing. Maybe you should share more code to check it.
I tried to follow your style, however I changed small things.
You can found the project I created to test your code here.
My app has a registration view, I compare strings in password textfield and confirm password textfield , if they dont match I want user go back to password textfield
This question done it with using tags
UITextField jump to previous
Is there a way to accomplish this without using tags?
//call next textfield on each next button
- (BOOL) textFieldShouldReturn:(UITextField *) textField {
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
if ([textField isKindOfClass:[SOTextField class]])
dispatch_async(dispatch_get_current_queue(),
^ { [[(SOTextField *)textField nextField] becomeFirstResponder]; });
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
if (textField==self.confirmPassword) {
if ([self.password.text isEqualToString:self.confirmPassword.text]) {
NSLog(#"password fields are equal");
}
else{
NSLog(#"password fields are not equal prompt user to enter correct values");
//[self.password becomeFirstResponder]; doesnt work
}
}
}
Without using tags, you could create an NSArray of textfield outlets the describes the desired ordering.
Declare and init like this ...
#property(nonatomic,strong) NSArray *textFields;
- (NSArray *)textFields {
if (!_textFields) {
// just made these outlets up, put your real outlets in here...
_textFields = [NSArray arrayWithObjects:self.username, self.password, nil];
}
return _textFields;
}
You'll need to fetch the text field that currently has focus, if there is one...
- (UITextField *)firstResponderTextField {
for (UITextField *textField in self.textFields) {
if ([textField isFirstResponder]) return textField;
}
return nil;
}
Then advance focus like this...
- (void)nextFocus {
UITextField *focusField = [self firstResponderTextField];
// what should we do if none of the fields have focus? nothing
if (!focusField) return;
NSInteger index = [self.textFields indexOfObject:textField];
// advance the index in a ring
index = (index == self.textFields.count-1)? 0 : index+1;
UITextField *newFocusField = [self.textFields objectAtIndex:index];
[newFocusField becomeFirstResponder];
}
And move focus backward like this...
- (void)previousFocus {
UITextField *focusField = [self firstResponderTextField];
// what should we do if none of the fields have focus? nothing
if (!focusField) return;
NSInteger index = [self.textFields indexOfObject:textField];
// backup the index in a ring
index = (index == 0)? self.textFields.count-1 : index-1;
UITextField *newFocusField = [self.textFields objectAtIndex:index];
[newFocusField becomeFirstResponder];
}