Getting and Setting Cursor Position of UITextField and UITextView in Swift
The following content applies to both UITextField
and UITextView
.
Useful information
The very beginning of the text field text:
let startPosition: UITextPosition = textField.beginningOfDocument
The very end of the text field text:
let endPosition: UITextPosition = textField.endOfDocument
The currently selected range:
let selectedRange: UITextRange? = textField.selectedTextRange
Get cursor position
if let selectedRange = textField.selectedTextRange {
let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
print("\(cursorPosition)")
}
Set cursor position
In order to set the position, all of these methods are actually setting a range with the same start and end values.
To the beginning
let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
To the end
let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
To one position to the left of the current cursor position
// only if there is a currently selected range
if let selectedRange = textField.selectedTextRange {
// and only if the new position is valid
if let newPosition = textField.position(from: selectedRange.start, offset: -1) {
// set the new position
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
To an arbitrary position
Start at the beginning and move 5 characters to the right.
let arbitraryValue: Int = 5
if let newPosition = textField.position(from: textField.beginningOfDocument, offset: arbitraryValue) {
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
Related
Select all text
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
Select a range of text
// Range: 3 to 7
let startPosition = textField.position(from: textField.beginningOfDocument, offset: 3)
let endPosition = textField.position(from: textField.beginningOfDocument, offset: 7)
if startPosition != nil && endPosition != nil {
textField.selectedTextRange = textField.textRange(from: startPosition!, to: endPosition!)
}
Insert text at the current cursor position
textField.insertText("Hello")
Notes
Use
textField.becomeFirstResponder()
to give focus to the text field and make the keyboard appear.See this answer for how to get the text at some range.
See also
- How to Create a Range in Swift
How to set cursor position for UITextView on user input?
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.
How to find the cursor Y position in an UITextView having multiple lines as word wraps?
You can get the cursor's CGPoint (X and Y position) within a UITextView in different ways. But do you need to find the position of cursor in relation to self.view (or phone screen borders)? If so, I translated this answer to C# for you:
var textView = new UITextView();
UITextRange selectedRange = textView.SelectedTextRange;
if (selectedRange != null)
{
// `caretRect` is in the `textView` coordinate space.
CoreGraphics.CGRect caretRect = textView.GetCaretRectForPosition(selectedRange.End);
// Convert `caretRect` in the main window coordinate space.
// Passing `nil` for the view converts to window base coordinates.
// Passing any `UIView` object converts to that view coordinate space.
CoreGraphics.CGRect windowRect = textView.ConvertRectFromCoordinateSpace(caretRect, null);
}
else
{
// No selection and no caret in UITextView.
}
Getting the cursor position of UITextField in ios
UITextField
conforms to the UITextInput
protocol which has methods for getting the current selection. But the methods are complicated. You need something like this:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField.tag == 201) {
UITextRange *selRange = textField.selectedTextRange;
UITextPosition *selStartPos = selRange.start;
NSInteger idx = [textField offsetFromPosition:textField.beginningOfDocument toPosition:selStartPos];
[myclass selectTextForInput:textField atRange:NSMakeRange(idx, 0)];
}
}
Pixel-Position of Cursor in UITextView
It's painful, but you can use the UIStringDrawing
additions to NSString
to do it. Here's the general algorithm I used:
CGPoint origin = textView.frame.origin;
NSString* head = [textView.text substringToIndex:textView.selectedRange.location];
CGSize initialSize = [head sizeWithFont:textView.font constrainedToSize:textView.contentSize];
NSUInteger startOfLine = [head length];
while (startOfLine > 0) {
/*
* 1. Adjust startOfLine to the beginning of the first word before startOfLine
* 2. Check if drawing the substring of head up to startOfLine causes a reduction in height compared to initialSize.
* 3. If so, then you've identified the start of the line containing the cursor, otherwise keep going.
*/
}
NSString* tail = [head substringFromIndex:startOfLine];
CGSize lineSize = [tail sizeWithFont:textView.font forWidth:textView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
CGPoint cursor = origin;
cursor.x += lineSize.width;
cursor.y += initialSize.height - lineSize.height;
return cursor;
}
I used [NSCharacterSet whitespaceAndNewlineCharacterSet]
to find word boundaries.
This can also be done (presumably more efficiently) using CTFrameSetter
in CoreText
, but that is not available in iPhone OS 3.1.3, so if you're targeting the iPhone you will need to stick to UIStringDrawing
.
How to move cursor of UITextView to TOUCHED LOCATION?
- (void) editTextRecognizerTabbed:(UITapGestureRecognizer *) aRecognizer
{
if (aRecognizer.state==UIGestureRecognizerStateEnded)
{
//Not sure if you need all this, but just carrying it forward from your code snippet
textview.editable = YES;
textview.dataDetectorTypes = UIDataDetectorTypeNone;
[textview becomeFirstResponder];
//Consider replacing self.view here with whatever view you want the point within
CGPoint point = [aRecognizer locationInView:self.view];
UITextPosition * position=[textView closestPositionToPoint:point];
[textView setSelectedTextRange:[textView textRangeFromPosition:position toPosition:position]];
}
}
Related Topics
Custom Url Scheme for New Facebook iOS App
Populating Tableview with Multiple Sections and Multiple Dictionary in an Array in Swift
Nspredicate to Test for Null, and Blank Strings
Prevent Duplicate Symbols When Building Static Library with Cocoapods
Conflicting Return Type in Implementation of 'Supportedinterfaceorientations': - Warning
Setting Alpha on Uiview Sets the Alpha on Its Subviews Which Should Not Happen
Undefined Symbols for Architecture Armv7 When Using Zxing Library in Xcode 4.5
Scnbox Different Colour or Texture on Each Face
How to Change the Highlighted Color of a Uibutton
How to Get iOS Appstorereceipturl into Base 64 Encoded String
Objective C - Pass by Value and Pass by Reference
How to Set-Up in App Purchase Free Trial Period in iOS App
Uitextfield Only Top and Bottom Border
To Create a New Uiwindow Over the Main Window
Cellforitematindexpath Not Called But Numberofitemsinsection Does
"File Not Found", "Linker Command Failed with Exit Code 1" in Xcode 4.5.1