I have a fairly bog-standard UI with a text field embedded in a few subviews. I'm seeing an issue where I can't move the cursor once I've typed in the text field, it continually just moves back to the start of the input.
At no point anywhere in the code are any methods called to change the editing position, like setSelectedTextRange or similar.
Please excuse the Objective-C, this is a legacy codebase!
self.textField = [UITextField new];
self.textField.text = element.value;
self.textField.placeholder = element.placeholder;
self.textField.returnKeyType = UIReturnKeyNext;
self.textField.delegate = self;
self.textField.autocorrectionType = element.autocorrectionType;
self.textField.autocapitalizationType = element.autocapitalizationType;
self.textField.secureTextEntry = element.secureTextEntry;
self.textField.keyboardType = element.keyboardType;
[self.textField addTarget:self action:#selector(handleTextChange:) forControlEvents:UIControlEventEditingChanged];
[self addSubview:self.textField];
- (void)handleTextChange:(UITextField *)sender
{
self.inputElement.value = sender.text;
// If we're showing the validation warning, give real time feedback to user
if (self.isShowingValidationWarning) {
[self validate];
}
}
- (void)validate
{
BOOL isValid = self.inputElement.isValid;
[self showValidationHint:!isValid animated:YES];
}
- (void)showValidationHint:(BOOL)show animated:(BOOL)animated
{
self.isShowingValidationWarning = show;
CGFloat duration = 0.0;
if (animated) {
duration = 0.2;
}
[UIView animateWithDuration:duration animations:^{
if (show) {
self.characterCountLabel.alpha = 0.0;
self.validationButton.alpha = 1.0;
self.validationButton.transform = CGAffineTransformMakeScale(1.0, 1.0);
} else {
self.characterCountLabel.alpha = 1.0;
self.validationButton.alpha = 0.0;
self.validationButton.transform = CGAffineTransformMakeScale(0.1, 0.1);
}
}];
}
inputElement.value doesn't have a setter or getter function, so nothing funky going on there!
The answer here was exactly what #juanreyesv pointed out in the comments! becomeFirstResponder call was moved to viewDidAppear rather than viewWillAppear and now it's working!
Related
How can you create a UITextField very similar to the one apple uses in their messaging app, Instagram uses, edmodo uses, Whatsapp uses and many other message sharing applications? This text field should animate up, animate down, be able to expand, only reach a certain height, and have other properties as well.
I researched for a while, but couldn't find an answer so I just created my own way to do it. There are several things you must do. First of all, this does use a UITextView and not a UITextField. Secondly, this code was written specifically for the iPhone 5, so you may need to play around with the coordinates. It does not use sizing classes or constraints. 1You need to implement the first. Your .h file should look like the following:
int returnPressed = 0;
int newLine;
#interface ViewController : UIViewController <UITextViewDelegate>
{
IBOutlet UIView *dock;
IBOutlet UIButton *sendButton;
IBOutlet UITextView *textView1;
CGRect previousRect;
}
#end
In the .m file there are many needs that need to be met to make it look how the iPhone message app looks. You'll notice how we have created our own custom place holder in here because textview don't really have them. Here is the .m file:
- (void)viewDidLoad {
[super viewDidLoad];
previousRect = CGRectZero;
textView1.delegate = self;
self->textView1.layer.borderWidth = 1.0f;
self->textView1.layer.borderColor = [[UIColor lightGrayColor] CGColor];
self->textView1.layer.cornerRadius = 6;
self->textView1.textColor = [UIColor lightGrayColor];
self->textView1.text = #"Place Holder";
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:#"\n"]) {
returnPressed +=1;
if(returnPressed < 17){
textView1.frame = CGRectMake(8, 8, textView1.frame.size.width, textView1.frame.size.height + 17);
newLine = 17*returnPressed;
[UIView animateWithDuration:0.1 animations:^
{
dock.transform = CGAffineTransformMakeTranslation(0, -250 - newLine);
}
];
}
}
return YES;
}
- (void)textViewDidChange:(UITextView *)textView{
UITextPosition* pos = textView1.endOfDocument;
CGRect currentRect = [textView1 caretRectForPosition:pos];
if (currentRect.origin.y > previousRect.origin.y || [textView1.text isEqualToString:#"\n"]){
returnPressed +=1;
if(returnPressed < 17 && returnPressed > 1){
textView1.frame = CGRectMake(8, 8, textView1.frame.size.width, textView1.frame.size.height + 17);
newLine = 17*returnPressed;
[UIView animateWithDuration:0.1 animations:^
{
dock.transform = CGAffineTransformMakeTranslation(0, -250 - newLine);
}
];
}
}
previousRect = currentRect;
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textField {
if([textView1.text isEqualToString:#""] || [textView1.text isEqualToString:#"Place Holder"]){
textView1.text = #"";
}
textView1.textColor = [UIColor blackColor];
[UIView animateWithDuration:0.209 animations:^
{
dock.transform = CGAffineTransformMakeTranslation(0, -250 - newLine);
}
completion:^(BOOL finished){}];
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if(touch.phase == UITouchPhaseBegan) {
[textView1 resignFirstResponder];
[self.view endEditing:YES];
int height = returnPressed*20;
[UIView animateWithDuration:0.209 animations:^
{
dock.transform = CGAffineTransformMakeTranslation(0, -height);
}];
if([textView1.text isEqualToString:#""]){
self->textView1.textColor = [UIColor lightGrayColor];
self->textView1.text = #"Place Holder";
}
}
}
That is everything. I hope this helps anyone who was trying to do this and I hope you can integrate this into an app of yours.
There is a pod that you can use for growing text view's :
https://cocoapods.org/pods/HPGrowingTextView
With this you can manage animation and maximum height of text view.
I have this problem, i have set in the Storyboard 2 UIButton and after touching one of them they must move, so i implemented this using animation, and works fine. But after the completion of the animation i'm performing a modal segue, the problem is that while modal segue is starting, raising the new UIView from the bottom, the 2 UIButton return to their previous place, where i set them in the Storyboard, and it's orribile to see. How could i solve this?
Here's the code i'm using
#property (strong, nonatomic) IBOutlet UIButton *maleBtn;
#property (strong, nonatomic) IBOutlet UIButton *femaleBtn;
- (void)viewDidLoad
{
[super viewDidLoad];
[_maleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
[_femaleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
}
- (IBAction)chooseGender:(id)sender
{
CGRect maleFrame;
CGRect femaleFrame;
if (sender == _maleBtn) {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = self.view.bounds.size.height;
}
else {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = -self.view.bounds.size.height;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
}
[UIView animateWithDuration:0.5
delay:0.5
options:UIViewAnimationCurveEaseOut
animations:^{
_maleBtn.frame = maleFrame;
_femaleBtn.frame = femaleFrame;
}
completion:^(BOOL finished){
if(sender == _maleBtn)
[self performSegueWithIdentifier:#"toMale" sender:self];
else
[self performSegueWithIdentifier:#"toFemale" sender:self];
}];
}
At the top you make a bool or add a property in your .h if you like. But I would just use a local variable for this.
BOOL didAnimate;
In viewDidLoad you set it
- (void)viewDidLoad
{
[super viewDidLoad];
//Set the bool value here to no;
didAnimate = NO;
[_maleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
[_femaleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
}
Use it here, and I also took the liberty to rewrite your code a bit.
- (IBAction)chooseGender:(id)sender
{
CGRect maleFrame;
CGRect femaleFrame;
if (sender == _maleBtn) {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = self.view.bounds.size.height;
}
else {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = -self.view.bounds.size.height;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
}
[UIView animateWithDuration:0.5
delay:0.5
options:UIViewAnimationCurveEaseOut
animations:^{
_maleBtn.frame = maleFrame;
_femaleBtn.frame = femaleFrame;
}
completion:^(BOOL finished){
didAnimate = YES;
}];
if(sender == _maleBtn && didAnimate){
[self performSegueWithIdentifier:#"toMale" sender:self];
}
else if((sender == _femaleBtn && didAnimate)){
[self performSegueWithIdentifier:#"toFemale" sender:self];
}
else{
NSLog(#"failure to transition");
}
}
I have not tested this and there might be spelling errors and such. So copy/paste might not work.
In my app i have a viewcontroller with a calendar in it (CKCalendar). Only one month is shown at the same time and with scrolling one can switch months. On certain days in the calendar are events which a highlighted by a circle.
Months are switched with the code
- (void) nextMonth:(UISwipeGestureRecognizer *)swipe {
[self.calView moveCalendarToNextMonth];
[self drawItemsForCurrentMonth];
}
The method drawItemsForCurrentMonth calls the next piece of code for every items that needs highlighting.
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSArray* res = [self calculateDayCirclePosition:date];
CGFloat x = [[res objectAtIndex:0] floatValue] + (DEFAULT_CELL_WIDTH-DEFAULT_CIRCLE_WIDTH-CELL_BORDER_WIDTH)/2;
CGFloat y = [[res objectAtIndex:1] floatValue] + (DEFAULT_CELL_HEIGHT-DEFAULT_CIRCLE_WIDTH)/2;
AgendaCircleView* circleView = [[AgendaCircleView alloc] initWithFrame:CGRectMake(x,y,self.circleWidth,self.circleWidth)];
circleView.date = date;
AgendaViewController* avc;
if([appDelegate.viewController.frontViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navVC = (UINavigationController*)appDelegate.viewController.frontViewController;
avc = (AgendaViewController*)[navVC visibleViewController];
}
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:avc action:#selector(didSelectDate:)];
tapGesture.enabled = YES;
tapGesture.delegate = avc;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[circleView addGestureRecognizer:tapGesture];
circleView.userInteractionEnabled = YES;
circleView.alpha = 0.5;
circleView.layer.cornerRadius = (self.circleWidth/2);
if(fill) {
circleView.backgroundColor = [appDelegate.dataController.rijschoolItem getColor1];
}
else {
circleView.layer.borderWidth = 1.0f;
circleView.backgroundColor = [UIColor clearColor];
circleView.layer.borderColor = [UIColor whiteColor].CGColor;
}
[circleView setNeedsDisplay];
[self.calendarContainer addSubview:circleView];
[self.calendarContainer bringSubviewToFront:circleView];
[self.calendarContainer setUserInteractionEnabled:YES];
This code works fine when the viewcontroller is initialized. Clicking on the circle fires the didSelectDate method. However when i change months the tapgesturerecognizer does not work anymore. When months are changed i use the exact same code that is used when initializing the viewcontroller. The strange thing is that when i open an other detailed viewcontroller and throw it on the view stack and go back, it is working again.
I've tried to use
gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer but this does not solve the problem.
Furthermore i've tried to use setNeedsLayout but this is not working either.
Any ideas?
Ok, I found the problem. So for others who might encounter the same type of problem. You have to make sure that the view is not animating while adding the gesturerecognizers.
I had this block of code
[UIView animateWithDuration:0.4 animations:^{
calendar.alpha = 1.0;
}];
[self drawItemsForCurrentMonth];
changed into
[UIView animateWithDuration:0.4 animations:^{
calendar.alpha = 1.0;
} completion:^(BOOL finished) {
if(finished) {
[self drawItemsForCurrentMonth];
}
}];
Having the method drawItemsForCurrentMonth after the animation block does not necessarily guarantee that the block is has actually finished. Luckily there is a completion block which covers that aspect.
I have made a game of Tic-Tac-Toe. If the game ends in a tie, an animated hudView appears for a couple of seconds. Then the game starts over, and that's when my problems occur. It doesn't respond to me tapping on screen to draw 'X' or 'O''s anymore. My suspicion is that the hudView is still there. I have tried different things to remove it, with no luck.
+ (instancetype)hudInView:(UIView *)view animated:(BOOL)animated
{
HudView *hudView = [[HudView alloc] initWithFrame:view.bounds];
hudView.opaque = NO;
[view addSubview:hudView];
view.userInteractionEnabled = NO;
[hudView showAnimated:YES];
return hudView;
}
And the animation:
- (void)showAnimated:(BOOL)animated
{
if (animated) {
self.alpha = 0.0f;
self.transform = CGAffineTransformMakeScale(1.3f, 1.3f);
[UIView animateWithDuration:4.5 animations:^{
self.alpha = 1.0f;
self.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.alpha = 0.0f;
}];
}
}
In the completion block, I have tried the following:
[self.superview removeFromSuperview];
In my ViewController where all of this gets called I've tried:
HudView *hudView = [HudView hudInView:self.view animated:YES];
hudView.text = #"\tIt's a Tie\n";
[hudView removeFromSuperview];
[self.view removeFromSuperview]
[hudView.superview removeFromSuperview];
Nothing I've tried so far is working. Any help would be much appreciated.
I'm new to iOS, i want to update the text in ViewDidLoad() function.
This is my button function, When button is clicked animation take place and also adds the value "1" to "resultText.text"
- (IBAction)oneButton1:(id)sender {
oneBtn2.userInteractionEnabled = YES;
CGRect frame = oneBtn1.frame;
CGRect frame1 = reffButton.frame;
frame.origin.x = frame1.origin.x;
frame.origin.y = frame1.origin.y;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration: 3.0];
[UIView animateWithDuration:3.0 animations:^{
[oneBtn1 setTransform:CGAffineTransformMakeScale(.4, .4)];
} completion:^(BOOL finished) {
oneBtn1.hidden = YES;
price = [resultText.text intValue];
[resultText setText:[NSString stringWithFormat:#"%i", price+1]];
}];
oneBtn1.frame = frame;
[UIView commitAnimations];
}
Problem: The above text value is 1 but in ViewDidLoad is 0 ,
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", resultText.text); // output is 0 instead of 1;
}
Please anyone tell me how to update the text value in ViewDidLoad function ...
ViewDidLoad calling only once when the object is creating .So you can't update the thing in ViewDidLoad.ViewDidLoad use for initialising the parameters and set the initial settings when an object creating.
This is because every time your view will load it create new object of your textField, thats why you are unable to fetch previous (because its new textField not old one).So you have to save your text some where, for example you can use NSUserDefaults
SetText
NSString *result=[NSString stringWithFormat:#"%i", price+1];
[resultText setText:];
//Also set it to NSUserDefaluts
[[NSUserDefaults standardUserDefaults] setValue:result forKey:#"key"];
[[NSUserDefaults standardUserDefaults] synchronize];
Get Text
- (void)viewDidLoad
{
[resultText setText:[[NSUserDefaults standardUserDefaults] valueForKey:#"key"]];
NSLog(#"%#", resultText.text);
}
EDIT
You can make animation afterButton click, so call this method in button click event
-(void)animateImage
{
if ([resultText.text isEqualToString:#"3"]) {
//make your animation
}
}
You can use a method to do that
- (void)updateLabel {
oneBtn2.userInteractionEnabled = YES;
CGRect frame = oneBtn1.frame;
CGRect frame1 = reffButton.frame;
frame.origin.x = frame1.origin.x;
frame.origin.y = frame1.origin.y;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration: 3.0];
[UIView animateWithDuration:3.0 animations:^{
[oneBtn1 setTransform:CGAffineTransformMakeScale(.4, .4)];
} completion:^(BOOL finished) {
oneBtn1.hidden = YES;
price = [resultText.text intValue];
[resultText setText:[NSString stringWithFormat:#"%i", price+1]];
}];
oneBtn1.frame = frame;
[UIView commitAnimations];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateLabel];
NSLog(#"%#", resultText.text); // output is 0 instead of 1;
}