How to set custom cursor position of UITextField? - ios

When user clicks on UITextFiled the cursor is at starting point, for design purpose its not look good. So, I want to change postion of cursor +5 or from center of UITextField.
I had implemented this code found from Github, Stil its not working. Please help me if you can guide me how to solve this problem.
My code is below
#import "UITextField+Selection.h"
#implementation UITextField (Selection)
- (NSRange)selectedRange
{
UITextPosition* beginning = self.beginningOfDocument;
UITextRange* selectedRange = self.selectedTextRange;
UITextPosition* selectionStart = selectedRange.start;
UITextPosition* selectionEnd = selectedRange.end;
NSInteger location = [self offsetFromPosition:beginning toPosition:selectionStart];
NSInteger length = [self offsetFromPosition:selectionStart toPosition:selectionEnd];
return NSMakeRange(location, length);
}
- (void)setSelectedRange:(NSRange)range
{
UITextPosition* beginning = self.beginningOfDocument;
UITextPosition* startPosition = [self positionFromPosition:beginning offset:range.location];
UITextPosition* endPosition = [self positionFromPosition:beginning offset:range.location + range.length];
UITextRange* selectionRange = [self textRangeFromPosition:startPosition toPosition:endPosition];
[self setSelectedTextRange:selectionRange];
}
#import "UITextField+Selection.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.nameTextField setSelectedRange:NSMakeRange(5,0)]; // Category method called
}
#end

Instead of moving the cursor you can add padding to the left of the UITextFiled like this for design to look better
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 20)];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;

If you want to have custom position of cursor when user selects UITextField you need to register for notification that is posted when user selects UITextField.
So you can add yourself as observer (for example in viewDidLoad method) via:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldSelected) name:UITextFieldTextDidBeginEditingNotification object:nil];
And then in textFieldSelected method you can:
- (void)textFieldSelected {
[self.nameTextField setSelectedRange:NSMakeRange(5,0)];
}

For designing purpose, You can put Image behind UITextview, and put textview above UIImage view. It will look good for design

Related

UITextField control losing delegate reference ID

I am adding controls to a ViewController by code (not XIB).
Form has two UITextfield controls which are defined as IBOutlet in the .h file
#interface ConditionsViewController : UIViewController <UITabBarDelegate, UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource>
{
id tfDelegate;
IBOutlet UITextField *tfOAT;
IBOutlet UITextField *tfWind;
}
- (void) setTextFieldAttributes:(UITextField *)tf;
...
In the .m within viewDidLoad, both UITextField are init and alloc
[super viewDidLoad];
tfDelegate = self;
//create OAT textfield
frame = CGRectMake( xRightSide-boxWidth*1.5-5, 200, boxWidth*0.75, boxHeight );
tfOAT = [[UITextField alloc] initWithFrame:frame];
[tfOAT setFont:font];
[self setTextFieldAttributes:tfOAT];
[[self view] addSubview:tfOAT];
NSLog(#"OAT after addSubview is: %#",tfOAT);
//create Wind textfield
frame = CGRectMake( xRightSide-boxWidth*1.5-5, 400, boxWidth*0.75, boxHeight );
UITextField *tfWind = [[UITextField alloc] initWithFrame:frame];
[tfWind setFont:font];
[self setTextFieldAttributes:tfWind];
//[tfWind setText:#"1"];
[[self view] addSubview:tfWind];
NSLog(#"WindField after addSubview is: %#",tfWind);
In the viewWillAppear
// OAT is returned
float t = [pModel OAT]; //t returns 12
if( t == 0 )
[tfOAT setText:#""];
else
[tfOAT setText:[NSString stringWithFormat:#"%.0f", t]];
NSLog(#"OAT within viewWillAppear is: %#",tfOAT);
NSLog(#"OAT text is: %#",tfOAT.text);
// Wind component is returned
int w = [pModel wind]; //w does return an integer 8
if(w==0)
[tfWind setText:#"0"];
else
[tfWind setText:[NSString stringWithFormat:#"%d",w]];
NSLog(#"WindField in viewDidAppear: %#",tfWind);
NSLog(#"Windfield text is: %#",tfWind.text);
Here is the setTextAttributes function, doesn't do anything special.
- (void) setTextFieldAttributes:(UITextField *)tf
{
tf.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
tf.borderStyle = UITextBorderStyleRoundedRect;
[tf setDelegate:self];
tf.clearsOnBeginEditing = YES;
[tf setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
[tf setReturnKeyType:UIReturnKeyDone];
}
The NSLog prints out as such. Note that the objects have pointer/reference numbers 0x7fccad7219a0 and 0x7fccb041beb0.
//NSLogs within viewDidLoad
OAT after addSubview is: <UITextField: 0x7fccad7219a0; frame = (510.5 200; 71.25 55); text = ''; opaque = NO; layer = <CALayer: 0x60000328f0e0>>
WindField after addSubview is: <UITextField: 0x7fccb041beb0; frame = (510.5 400; 71.25 55); text = ''; opaque = NO; layer = <CALayer: 0x600003284860>>
//NSLogs within viewWillAppear
OAT within viewWillAppear is: <UITextField: 0x7fccad7219a0; frame = (510.5 200; 71.25 55); text = '12'; opaque = NO; tag = 3; layer = <CALayer: 0x60000328f0e0>>
OAT text is:12
WindField within viewDidAppear: (null)
Windfield text is: (null)
Note that the pointer or reference to tfWind has vanished somewhere between viewDidLoad and viewWillAppear. In the navigator window under the tfWind object, the entry _delegate = (id) 0x0 appears after a break in viewWillAppear but shows the reference number for tfOAT. Both controls are still on the screen but tfWind has become "disconnected".
If I uncomment the //[tfWind setText:#"1"]; line in the viewDidLoad (restart the app), I indeed do get the "1" to appear within the textfield.
This is a real mystery. Anyone have any ideas or seen this before?
Ah...
The reason it has "become disconnected" is because you are creating a new variable in viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
// creates a text field object for the class property tfOAT
tfOAT = [[UITextField alloc] initWithFrame:frame];
...
// creates a NEW LOCAL text field object
UITextField *tfWind = [[UITextField alloc] initWithFrame:frame];
...
// end of viewDidLoad, the LOCAL tfWind goes out-of-scope
}
See the difference?

Adding textfield below another textfield programmatically in iOS

In my app I want to programmatically add textfield below another if required on click of a button. I already had provided two textFields. if a user want to add another textfield he can do so by clicking a button. I have already written code to obtain the textfield but the problem is that it overlaps the already designed textFields. How can I do it?
Is there any way through which I can get the x and Y co-ordinates of already designed textfield so that I can place new textField relative to those co-ordinates.
This code add textField to view dynamically when every click action on button
ExampleViewController.h
#import <UIKit/UIKit.h>
#interface ExampleViewController :UIViewController<UITextFieldDelegate>
#property int positionY;
#property int fieldCount;
#property (strong,nonatomic) UIScrollView *scroll;
#end
ExampleViewController.m
#import "ExampleViewController.h"
#interface ExampleViewController ()
#end
#implementation ExampleViewController
#synthesize positionY;
#synthesize fieldCount;
#synthesize scroll;
- (void)viewDidLoad {
[super viewDidLoad];
scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.backgroundColor = [UIColor whiteColor];
[self.view addSubview:scroll];
UIButton *clickToCreateTextField = [[UIButton alloc] initWithFrame:CGRectMake(40, 80, self.view.frame.size.width-80, 75)];
[clickToCreateTextField setTitle:#"Create Text Field" forState:UIControlStateNormal];
[clickToCreateTextField addTarget:self action:#selector(clickedButton) forControlEvents:UIControlEventTouchUpInside];
[clickToCreateTextField setBackgroundColor:[UIColor blackColor]];
[scroll addSubview:clickToCreateTextField];
positionY = clickToCreateTextField.center.y;
fieldCount = 0;
// Do any additional setup after loading the view.
}
-(void) clickedButton{
//add text field programmitacally
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(40, positionY, self.view.frame.size.width-80, 75)];
textField.delegate = self;
//give a tag to determine the which textField tapped
textField.tag = fieldCount;
textField.placeholder = [NSString stringWithFormat:#"Your dynamically created textField: %d", fieldCount ];
[scroll addSubview:textField];
//check if the textFields bigger than view size set scroll size and offset
if (positionY>= self.view.frame.size.height) {
scroll.contentOffset = CGPointMake(0, positionY);
scroll.contentSize = CGSizeMake(scroll.frame.size.width, scroll.frame.size.height+positionY);
}
fieldCount++;
//increase the position with a blank place
positionY = positionY+textField.frame.size.height+20;
}
#pragma mark TextField Delegate Methods
-(void) textFieldDidBeginEditing:(UITextField *)textField{
//Do what ever you want
}
-(void) textFieldDidEndEditing:(UITextField *)textField{
[textField resignFirstResponder];
//do anything
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
-(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
You can do any additional changes on this code.
I think this example explain your answer.
Hope it helps.
Use a counter and calculate y like this counter*texfield.frame.size.height.

Create UITextField on UIView but can not get text input

I am using Xcode 6 and doing a small project on iOS 8, and I need to render some text onto the View.
My method is to create a UITextField on a UIView and as long as people type onit, the app redraw the View:
- (void)viewDidLoad {
[super viewDidLoad];
self.renderTextView.textToRender = #"Please type something…";
UITextField* textField = [[UITextField alloc] initWithFrame:self.renderTextView.frame];
textField.opaque = NO;
[self.renderTextView addSubview:textField];
[textField addTarget:self action:#selector(updateLabelAndRefresh:) forControlEvents:UIControlEventEditingChanged];
}
- (void) updateLabelAndRefresh: (id)sender{
self.renderTextView.textToRender = #"Hello World";
// if text's length > 0 …
if (self.textField.text.length > 0) {
self.renderTextView.textToRender = self.textField.text;
}
[self.renderTextView setNeedsDisplay];
}
But the problem is: no matter how I try, I can not get the actual text I type on my phone. and the console showed me that the text is null. I kept googling it but I stil can not work it out.
Do you guys have any solution? Thank you ;)
In below some error is there, give a look.
- (void)viewDidLoad {
[super viewDidLoad];
//Here textField is local variable
UITextField* textField = [[UITextField alloc] initWithFrame:self.renderTextView.frame];
textField.opaque = NO;
}
- (void) updateLabelAndRefresh: (id)sender{
self.renderTextView.textToRender = #"Hello World";
// if text's length > 0 …
if (self.textField.text.length > 0) { //Then how come u getting self.textField here and using it as global one.
//So you won't get access to that locally defined textField in viewDidLoad method.
self.renderTextView.textToRender = self.textField.text;
}
}
Please check your code and update me about it.

iOS 7.1 UITextView still not scrolling to cursor/caret after new line

Since iOS 7, a UITextView does not scroll automatically to the cursor as the user types text that flows to a new line. This issue is well documented on SO and elsewhere. For me, the issue is still present in iOS 7.1. What am I doing wrong?
I installed Xcode 5.1 and targeted iOS 7.1. I'm using Auto Layout.
Here's how I position the text view's content above the keyboard:
- (void)keyboardUp:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
UIEdgeInsets contentInset = self.textView.contentInset;
contentInset.bottom = keyboardRect.size.height;
self.textView.contentInset = contentInset;
}
What I have tried: I have tried many of the solutions posted to SO on this issue as it pertains to iOS 7. All of the solutions that I have tried do not seem to hold up well for text views displaying an attributed string. In the following three steps, I outline how the most up-voted answer on SO (https://stackoverflow.com/a/19277383/1239263) responds to the user tapping the return key for the first time.
(1.) The text view became the first responder in viewDidLoad. Scroll to the bottom of the text view where the cursor is located.
(2.) Before typing a single character, tap the return key on the keyboard. The caret disappears out of sight.
(3.) Tapping the return key again, however, seems to normalize the situation. (Note: deleting the latter new line, however, makes the caret disappear once again).
Improved solution's code for UITextView descendant class:
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define is_iOS7 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")
#define is_iOS8 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")
#implementation MyTextView {
BOOL settingText;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self];
}
return self;
}
- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated {
CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
rect.size.height += textView.textContainerInset.bottom;
[textView scrollRectToVisible:rect animated:animated];
}
- (void)handleTextViewDidChangeNotification:(NSNotification *)notification {
if (notification.object == self && is_iOS7 && !is_iOS8 && !settingText) {
UITextView *textView = self;
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
}
- (void)setText:(NSString *)text {
settingText = YES;
[super setText:text];
settingText = NO;
}
Note it doesn't work when Down key is pressed on Bluetooth keyboard.
A robust solution should hold up in the following situations:
(1.) a text view displaying an attributed string
(2.) a new line created by tapping the return key on the keyboard
(3.) a new line created by typing text that overflows to the next line
(4.) copy and paste text
(5.) a new line created by tapping the return key for the first time (see the 3 steps in the OP)
(6.) device rotation
(7.) some case I can't think of that you will...
To satisfy these requirements in iOS 7.1, it seems as though it's still necessary to manually scroll to the caret.
It's common to see solutions that manually scroll to the caret when the text view delegate method textViewDidChange: is called. However, I found that this technique did not satisfy situation #5 above. Even a call to layoutIfNeeded before scrolling to the caret didn't help. Instead, I had to scroll to the caret inside a CATransaction completion block:
// this seems to satisfy all of the requirements listed above–if you are targeting iOS 7.1
- (void)textViewDidChange:(UITextView *)textView
{
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
Why does this work? I have no idea. You'll have to ask an Apple engineer.
For completeness, here's all of the code related to my solution:
#import "ViewController.h"
#interface ViewController () <UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UITextView *textView; // full-screen
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *string = #"All work and no play makes Jack a dull boy.\n\nAll work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:#{NSFontAttributeName: [UIFont fontWithName:#"Verdana" size:30.0]}];
self.textView.attributedText = attrString;
self.textView.delegate = self;
self.textView.backgroundColor = [UIColor yellowColor];
[self.textView becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardIsUp:) name:UIKeyboardDidShowNotification object:nil];
}
// helper method
- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated
{
CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
rect.size.height += textView.textContainerInset.bottom;
[textView scrollRectToVisible:rect animated:animated];
}
- (void)keyboardIsUp:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
UIEdgeInsets inset = self.textView.contentInset;
inset.bottom = keyboardRect.size.height;
self.textView.contentInset = inset;
self.textView.scrollIndicatorInsets = inset;
[self scrollToCaretInTextView:self.textView animated:YES];
}
- (void)textViewDidChange:(UITextView *)textView
{
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
#end
If you find a situation where this doesn't work, please let me know.
I solved it by getting the actual position of the caret and adjusting to it, here's my method:
- (void) alignTextView:(UITextView *)textView withAnimation:(BOOL)shouldAnimate {
// where the blinky caret is
CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
CGFloat offscreen = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);
CGPoint offsetP = textView.contentOffset;
offsetP.y += offscreen + 3; // 3 px -- margin puts caret 3 px above bottom
if (offsetP.y >= 0) {
if (shouldAnimate) {
[UIView animateWithDuration:0.2 animations:^{
[textView setContentOffset:offsetP];
}];
}
else {
[textView setContentOffset:offsetP];
}
}
}
If you only need to orient after the user presses return / enter, try:
- (void) textViewDidChange:(UITextView *)textView {
if ([textView.text hasSuffix:#"\n"]) {
[self alignTextView:textView withAnimation:NO];
}
}
Let me know if it works for you!
I can't find original source but it works on iOS7.1
- (void)textViewDidChangeSelection:(UITextView *)textView
{
if ([textView.text characterAtIndex:textView.text.length-1] != ' ') {
textView.text = [textView.text stringByAppendingString:#" "];
}
NSRange range0 = textView.selectedRange;
NSRange range = range0;
if (range0.location == textView.text.length) {
range = NSMakeRange(range0.location - 1, range0.length);
} else if (range0.length > 0 &&
range0.location + range0.length == textView.text.length) {
range = NSMakeRange(range0.location, range0.length - 1);
}
if (!NSEqualRanges(range, range0)) {
textView.selectedRange = range;
}
}
Some one have made a subclass that solves all scrolling0related issues in UITextView. The implementation couldn't be easier - switch UITextView with the subclass PSPDFTextView.
A post about it, showing what is fixed (With nice Gif animations) is here: Fixing UITextView on iOS 7
The git is here: PSPDFTextView

UIStepper issue: decrements but won't increment

I have a weird issue with a UIStepper (and it's accompanying UITextField)
Consider this code snippet:
#interface LTRPageTracker : UIView <UITextFieldDelegate>
{
UIStepper* page_move;
UITextField* page_no_view;
}
-(void) nextOrPrevPage:(id)sender forEvent:(UIControlEvents) event;
#implementation LTRPageTracker
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
CGRect stepperFrame, pageNoframe;
pageNoframe.origin = frame.origin;
pageNoframe.size.height = frame.size.height;
pageNoframe.size.width = frame.size.width/2;
stepperFrame.origin.x = pageNoframe.origin.x + pageNoframe.size.width +1;
stepperFrame.origin.y = frame.origin.y;
stepperFrame.size = pageNoframe.size;
page_move = [[UIStepper alloc] initWithFrame:stepperFrame];
[page_move setMinimumValue:0];
[page_move setValue:7];
page_move.maximumValue =1000;
[page_move addTarget:self action:#selector(nextOrPrevPage:forEvent:) forControlEvents:UIControlEventValueChanged];
page_no_view = [[UITextField alloc] initWithFrame:pageNoframe];
page_no_view.delegate = self;
page_no_view.backgroundColor = [UIColor redColor];
[self addSubview:page_no_view];
[self addSubview:page_move];
[page_move sendActionsForControlEvents:UIControlEventValueChanged];
[page_move setEnabled:YES];
}
return self;
}
-(void) nextOrPrevPage:(id) sender forEvent:(UIControlEvents) event {
//assert(sender == page_move);
NSLog(#"Event is %x", event);
page_no_view.text = [[NSNumber numberWithDouble: page_move.value] stringValue];
}
I have added this View to the navigation bar.
And I can decrement the value of the UIStepper but no increment it (the event will simply not get triggered for increment but will do so decrement).
WHY?
Using iOS 7, running on simulator.
Whenever you have a problem with a UI element not responding, the first thing you should check is whether it (or part of it) is outside the bounds of its superview (the part that's out of the bounds will not respond). An easy way to do this, is to give the superview a background color. In the init method assign a background color to self, and see what that shows you. I'm betting that you'll see that the right side of the stepper is not within its superview, so you'll need to make that view bigger, or change the stepper's position within that view.
If you make the view's frame 150x30, and change these two lines in the view's init method, I think it should work ok for a number as large as 1000:
pageNoframe.size.width = frame.size.width/4; // changed 2 to 4
stepperFrame.origin.x = pageNoframe.origin.x + pageNoframe.size.width+20; // changed 1 to 20

Resources