Can't set cursor position in UITextView - ios

I'm working on an app that is highly dependent on UITextView. My ideal behavior is when a user quickly presses a double space, the text cursor will jump ahead 5 spaces to make space for a UIButton. I've implemented the following code, but the cursor doesn't seem to jump even though all the other actions in the "quickSpace" area happen. Anyone know what I am doing wrong?
UPDATE: I think the problem is that I am trying to jump the cursor to a point outside of the range that currently exists. In other words, someone is entering new text into the UITextView and the range is set to wherever the cursor happens to be. Is that the end of the range? Is it possible to send a cursor outside of the range that currently exists?
if(text==" "){
let now=Date()
if let last=previousSpaceTimestamp
{
if now.timeIntervalSince(last)<0.3
{
//mainTextBox.text=textView.text.appending("hello")
isQuickSpace=true
var checkOffButton: UIButton=createCheckButton()
checkOffButton.backgroundColor=UIColor.green
checkOffButton.frame.size=CGSize(width: 20, height: 20)
//checkOffButton.frame.offsetBy(dx: 0, dy: CGFloat(buttonYPos))
mainTextBox.addSubview(checkOffButton)
buttonYPos=buttonYPos+15
if let currentRange=mainTextBox.selectedTextRange
{
if let newPosition=mainTextBox.position(from: currentRange.start,
offset: 200)
{
mainTextBox.selectedTextRange=mainTextBox.textRange(from: newPosition,
to: newPosition)
}
}
return false
}
}
previousSpaceTimestamp=now
}
else
{
previousSpaceTimestamp=nil
}
return true
}

Set the TextView Range Like this in Main Queue.
DispatchQueue.main.async {
mainTextBox.isEditable = true
mainTextBox.selectedRange = NSMakeRange(2, 0)
}

Related

iOS WKWebView findString not selecting and scrolling

Since iOS 14 WebKit supports findString, but there is no documentation whatsoever yet.
However on the WWDC Sessions Discover WKWebView enhancements they mention that is a basic functionality for "Find on Page", where you can find a string and the WebView will select it and scroll to center it.
It seems very easy to use and to be finding the string as I get a result of matchFound true, but there is no selection and there is no scrolling. Maybe I'm missing something?
This is the code I have tried:
let webView = WKWebView()
// ...
// after loading a website with the desired string on it.
// ...
webView.find("hello world") { result in
print(result.matchFound) // true
}
Update for iOS 16
On iOS 16 we have the new UIFindInteraction API and now it is possible and very easy to do a Find on Page feature and search for a string.
myWebView.isFindInteractionEnabled = true
myWebView.findInteraction?.presentFindNavigator(showingReplace: false)
Important
This is not supported on macOS.
Original Answer
So far I was only able to make it 'kind of working' combining with a bit of JavaScript.
let webView = WKWebView()
webView.select(nil)
webView.find("hello world") { result in
guard result.matchFound else { return }
webView.evaluateJavaScript(
"window.getSelection().getRangeAt(0).getBoundingClientRect().top") { offset, _ in
guard let offset = offset as? CGFloat else { return }
webView.scrollView.scrollRectToVisible(
.init(x: 0,
y: offset + webView.scrollView.contentOffset.y,
width: 100,
height: 100), animated: true)
}
}
Description:
1.
webView.select(nil) to make it first responder.
This is important otherwise when the match is found it won't be selected.
2.
webView.find("my string")
3.
If match is found use JavaScript to get the offset to the selected text.
4.
When receiving the offset scroll to it.

Is it possible to set the alignment of segmented Control titles to the left?

I have been looking around for a way to set the alignment of the segmented control titles to the left but I don't seem to be able to achieve what I want.
I have created this little function to change the frame of the subviews of the segment control.
It works at first.
func modifyFrameOfSegment() {
for segment in segmentedControl.subviews {
guard segment.subviews.isNotEmpty else { return }
segment.contentMode = .left
for label in segment.subviews where label is UILabel {
label.frame = CGRect(x: 0, y: label.frame.origin.y, width: label.frame.size.width, height: label.frame.size.height)
(label as! UILabel).textAlignment = .left
}
}
}
But everytime I select a new segment it resets the frames of all the subviews and center align all the titles again.
Is there a way to achieve a permanent left alignment for the segment titles in a segmented control?
Any tips or advice would be greatly appreciated.
Thank you for your time.
Let's use this method
self.segmentedControl.setContentPositionAdjustment(UIOffset(horizontal: -20, vertical: 0), forSegmentType: .left, barMetrics: .default)
And you can do what you want (Of course, you can change the horizontal & vertical value by your needs). Here is the result:
Update:
There's apparently no way to set the alignment of the items, but you can fake it by adjusting the position of each individual item using setContentOffset(_ offset: CGSize, forSegmentAt segment: Int). Here's a kludgy example:
class LeftSegmentedControl: UISegmentedControl {
var margin : CGFloat = 10
override func layoutSubviews() {
super.layoutSubviews()
leftJustifyItems()
}
func leftJustifyItems() {
let fontAttributes = titleTextAttributes(for: .normal)
let segments = numberOfSegments - 1
let controlWidth = frame.size.width
let segmentWidth = controlWidth / CGFloat(numberOfSegments)
for segment in 0...segments {
let title = titleForSegment(at: segment)
setWidth(segmentWidth, forSegmentAt: segment)
if let t = title {
let titleSize = t.size(withAttributes: fontAttributes)
let offset = (segmentWidth - titleSize.width) / 2 - margin
self.setContentOffset(CGSize(width: -offset, height: 0), forSegmentAt: segment)
}
}
}
}
Here's what it looks like:
There are a few caveats:
This version sets the segments to all have equal width, which might not be what you want.
I used a fixed left margin of 10px because it seems unlikely that you'd want to vary that, but you can obviously change it or make it a settable property.
Just because you can do this doesn't mean you should. Personally, I don't think it looks great, and it suffers in the usability department too. Users expect segmented control items to be centered, and left-justifying the items will make it harder for them to know where to tap to hit the segment. That seems particularly true for short items like the one labelled "3rd" in the example. It's not terrible, it just seems a little weird.
Original answer:
UIControl (of which UISegmentedControl is a subclass) has a contentHorizontalAlignment property that's supposed to tell the control to align its content a certain way, so the logical thing to do would be to set it like this:
let segmented = UISegmentedControl(items: ["Yes", "No", "Maybe"])
segmented.frame = CGRect(x:75, y:250, width:250, height:35)
segmented.contentHorizontalAlignment = .left
But that doesn't work — you still get the labels centered. If you've got a compelling use case for left-aligned segments, you should send the request to Apple.
One way you could work around this problem is to render your labels into images and then use the images as the segment labels instead of plain strings. Starting from the code in How to convert a UIView to an image, you could easily subclass UISegmentedControl to create images from the item strings.

Swift 4 UiTextView - delete a word inside textView on button click

Is there a way to delete a specific word inside a UITextView?
let's say for example that in a textView the user wrote: "Hello my nome is john".
As soon as he finished typing he noticed that he mistyped a word.
Lets' say that there is an array initialised with a set o word and "name" is one of this.
when the user go back with the cursor and start deleting the misspelled a list of suggestion comes up.
He detect the word name and click on it.
is there a way to delete the word nome and insert the word name on which he just clicked.
So basically is there a way to get the word immediately before the cursor and remove it from the text view?
I have been able to get the first word before the cursor:
func characterBeforeCursor() -> String? {
// get the cursor position
if let cursorRange = postTextField.selectedTextRange {
// get the position one character before the cursor start position
if let newPosition = postTextField.position(from: cursorRange.start, offset: -1) {
let range = postTextField.textRange(from: newPosition, to: cursorRange.start)
return postTextField.text(in: range!)
}
}
return nil
}
but i wouldn't know how to get the entire word (until the first space, or a particular character e.g "#" ) and delte it from the textview.
I am a bit lost.... so Thanks to anyone will help.

How can I move the cursor to the beginning of a UITextField in Swift?

I have seen this question asked many times, but every answer seems to be written in objective c, which I do not know nor do I know how to convert to Swift.
I have a text field where I want a user to input a percentage.
I have it so that when they start editing the text box, the placeholder text disappears and is replaced with a percentage sign.
I want this percentage sign to always remain at the end of the input. I can't seem to figure out how to move the cursor back to the beginning of the text box to achieve this.
Here's the code for my begin editing action (this includes another text box where the user inputs a dollar amount, but the dollar sign comes first so that's no big deal)
#IBAction func textBoxBeginEditing(sender: UITextField) {
// Dismiss keyboard if the main view is tapped
tapRecognizer.addTarget(self, action: "didTapView")
view.addGestureRecognizer(tapRecognizer)
// If there's placeholder text, remove it and change text color to black
if (sender.textColor == UIColor.lightGrayColor()) {
sender.text = nil
sender.textColor = UIColor.blackColor()
}
// Force the keyboard to be a number pad
sender.keyboardType = UIKeyboardType.NumberPad
// Set up symbols in text boxes
if (sender == deductibleTextBox) {
sender.text = "$"
}
if (sender == percentageTextBox) {
sender.text = "%"
// This part doesn't do anything... Need a solution
let desiredPosition = sender.beginningOfDocument
sender.selectedTextRange = sender.textRangeFromPosition(desiredPosition, toPosition: desiredPosition)
}
}
That last bit was all I got from the internet for help. This app I am creating has been quite the iOS learning curve, so I apologize if this is a dumb question.
let newPosition = textView.beginningOfDocument
textView.selectedTextRange = textView.textRangeFromPosition(newPosition, toPosition: newPosition)
In this we are getting the beginning of the textview and then setting the selected both to the beginning.

Inset text in UILabel from left

I have a UILabel which I am using to display multiple lines of text. At the moment when the text is displayed, it is right up against the left hand side of the label which doesn't look too great. I would like the text to be inset slightly from the left.
This is my code so far:
if notes.objectAtIndex(indexPath.row) as NSString == "" {
cell.notesLabel.text = "No notes to display."
cell.notesLabel.textAlignment = NSTextAlignment.Center
} else {
cell.notesLabel.textAlignment = NSTextAlignment.Left
}
I was looking at some Objective-C examples but I couldn't get them to work and I don't really think they were what I was looking for.
Also, I was trying to do the same thing with a different label and in that case I assumed I could have just added " " to the end of the string (as it is a single line label) to move it in from the right, but I was surprised to see that this doesn't work?
Thanks.
To inset the text from the left edge, you should create a UILabel subclass, and override drawTextInRect:,
class RDLabel: UILabel {
override func drawTextInRect(rect: CGRect) {
let newRect = CGRectOffset(rect, 10, 0) // move text 10 points to the right
super.drawTextInRect(newRect)
}
}

Resources