UITextView.highlight automatically scrolls to bottom of document - swift

I use a UITextView to display documents. Within the document, the user can tap on a word, and it will be highlighted in red.
This works fine, except when the document is bigger than the size of the UITextView. When I call this code:
myTextView.highlight(charPos: wordIndexInDocument, length: wordLength)
the word is correctly highlighted, but the document scrolls to the bottom.
I have tried the following, which was recommended elsewhere, but it does not work.
myTextView.isScrollEnabled = false
myTextView.highlight(charPos: wordIndexInDocument, length: wordLength)
myTextView.isScrollEnabled = true
If I comment out the statement with the highlight function, the selected word is not highlighted, but there is no scrolling. So that line is definitely causing the scrolling.
This code is located in a subclass of a UIView, not UIViewController.
I need the document to stay in place when the user highlights a word. Any ideas would be greatly appreciated.

try using scrollRangeToVisible and give the range of the highlighted text
textView.scrollRangeToVisible(NSRange(location: 0, length: 0))

Related

Another UIMenuController won't display problem

and thanks in advance for your help. I assure you that I've read most everything on here about UIMenuController problems. I really think I've covered it all. Clearly I've missed something.
In a nutshell, I'm trying to replicate the "Replace ..." edit menu behavior (but with my own function different than Replace). (If you're not familiar, when a word is selected, the Replace... option in the edit menu will bring up a second menu which shows possible alternate spellings for the word.)
In a UITextView (sub-classed), I select some text. The default gesture recognizer causes the edit menu to come up with the expected items, including my added "Translate..." option. When I click on "Translate..." in the menu, the menu closes and invokes my selector code. That code changes the menu items to the sub-choices I want. I call
UIMenuController.shared.showMenu(from: self, rect: textBounds).
I see the calls to canPerformAction() to verify that the "sub-menu" items I've added are recognized, but the menu never shows up. The notification for willShowWindowNotification (which occurs when the first menu is opened) does not happen for this sub-menu.
Here is the code:
#objc func translateSelectionMenu()
{
let sharedMC = UIMenuController.shared
// Create menu choices for the translate sub-menu.
let charChoice = UIMenuItem(title: "To Chars", action: #selector(translateChars))
let byteChoice = UIMenuItem(title: "Byte Decimal", action: #selector(translateByte))
let halfChoice = UIMenuItem(title: "2-Byte Decimal", action: #selector(translateHalf))
savedMenuItems = sharedMC.menuItems
sharedMC.menuItems = [charChoice, byteChoice, halfChoice]
... for brevity, I've omitted the code here which determines the bounds of the user's
text selection. The resulting numbers are shown below.
let textBounds = CGRect(x: 114.1, y: 73, width: 48, height: 55)
// let windowBounds = convert(textBounds, to: nil)
// sharedMC.update() not needed
self.becomeFirstResponder() // TextView is already the first responder. This does nothing.
sharedMC.showMenu(from: self, rect: textBounds)
}
Note that the TextView IS and must remain first-responder. (Changing it loses the users selection.) So I've implemented all of this in the subclass of the UITextView that is showing the user's text. I have tried using the UITextView-referenced bounds and the window-referenced bounds but neither works.
If I move one of the end-points of the selected text or just click in the selection, this causes the menu to be shown again, and it has my sub-menu items in it as expected. I know this should work because "Replace..." does it all the time.
Things I've verified:
My sub-class of UITextView is a UIView.
UserInteractionIsEnabled is true (since I can select the text).
There is only one window, but I am calling self.window.makeKeyAndVisible() at the point where canBecomeFirstResonder is called.
I have implemented canBecomeFirstResponder() (returning True). (It is called right before the gesture recognizer brings up the first menu but not after that.)
I do call self.becomeFirstResponder() (even though it already is).
I have implemented canPerformAction(). This is called a lot both with first-menu and sub-menu items. I return True for the items I want to use.
What else? Thanks!!
I asked Apple for help on this. The fix is to add
sharedMC.hideMenu()
right before the call to showMenu().
I think the issue is that my code is not what had presented the Menu originally and so I had to hide it before my code could show it. I note (from notifications) that the menu was not officially "hidden" at all (even though it was no longer visible after pressing my Translate... button).
I also tried just changing the menuItems and calling update(), but that also didn't work, probably again for the same reason.

How do I fit a paragraph in a scroll view controller programmatically?

I'm building an app and it has a description of everyone on the roster section. The text seems to overrun the page and I do not know the code or way to make it fill programmatically.
I have tried changing the values on the line of code the UITextField is in.
let descriptionM = UITextField(frame: CGRect(x: xPosition+15, y:
500, width: 300, height: 50))
descriptionM.text = desc[i]
descriptionM.textColor = UIColor.black
descriptionM.backgroundColor = UIColor.clear
descriptionM.contentMode = .scaleAspectFill
I wanted the text which is an array above, to fill the certain page on the scroll view instead it runs over the right side of the view with only the first line readable.
I have tried different values but non have worked.
I have tried changing the values on the line of code the UITextField is in.
You're using the wrong tool for the job. Text fields only ever handle one line, which is why there are no properties that let you set the number of lines.
I wanted the text which is an array above, to fill the certain page on the scroll view instead it runs over the right side of the view with only the first line readable.
Views that display text on multiple lines include UILabel and UITextView. UILabel is really meant for just a few lines or less, and it sounds like you may need many lines, so you should switch to UITextView. Or, if you're displaying a number of distinct items rather than paragraphs of text, you could use a UITableView where each cell displays a single item using a UILabel, UITextField, or UITextView. Table views are great for presenting arrays of data.

How to set first responder for NSTextView in Swift?

Edit: in a macOS project
I have a simple ViewController which I display as popover on a status item menu app.
I change the text of the view text with a NSTableView, depending of which item is clicked. The code I use is similar to this one:
mainTextField.insertText(newStr, replacementRange: theRange)
(I use insertText for the purpose to have the change recorded in undo manager)
Then I highlight the text:
// create the new NSRange
let range = NSRange(location: startRange, length: newStrLength)
// select the range in field
mainTextField.selectedRange = range
All work fine, except that the text is highlighted but with a light grey instead of the usual sky blue, indicating that the control is not the first responder. And when I click on the field the selection disappear.
Actually I would like that the NSTextView becomes first responder so I can directly copy the selected text.
Edit: if I press Tab key on the keyboard I got the textView to become first responder (and the grey selection becomes standard sky blue).
Corrected Answer
In AppKit, you need:
if mainTextField.acceptsFirstResponder {
mainTextField.window?.makeFirstResponder(mainTextField)
}
In this case, it's probably safe to not check acceptsFirstResponder, but it doesn't hurt either.
Original Answer (UIKit)
You need to call mainTextField.becomeFirstResponder().

Why will my label not change location when trying to add additional text field?

So I have a button on my storyboard. When that button is pressed, I want to move a certain label down a little bit and also add a new text field to the screen. Here is the simplified code:
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.addIngredientLabel.center.y += self.addIngredientLabel.bounds.height
}) { (_) -> Void in
let newIngredientTextField = UITextField(frame: CGRectMake(100, 110, 100, 100))
newIngredientTextField.center.y -= 50
newIngredientTextField.placeholder = "place text here"
newIngredientTextField.contentHorizontalAlignment = .Left
self.view.addSubview(newIngredientTextField)
}
The problem though, is that after the animation is completed, the label jumps back to its original location. It is like the animation is being pre-maturely terminated. The textField gets added and the label moves but it does not stay moved.
I have tried adding the text field at the end of the function instead of in a completion block. No kind of reordering seems to be helping.
I feel like I am not understanding something fundamental to animations. If anyone has any advice I would greatly appreciate it.
Since you're using the storyboard, you are most likely also using auto-layout. The positions of your controls are determined in your visual design (storyboard) and will adjust automatically to orientation changes and adapt to various device screen sizes. This is good but the flip size is that you cannot directly change the positions of your control using their x,y coordinates.
Instead, you can change values in the constraints you defined in the storyboard. for example, if you button has a "Top" distancs constraint with a control above it (or the edge of the view), you can change the constraint's "constant" attribute to indirectly change the position of the control.
In order to manipulate the contraints in your code, you can create IBOutlets for them just as you would do with any control.

Center Multi-Line Text on UIButton using IB

How do you center text line-by-line in a UIButton using Interface Builder? I am scouring the options and just don't see it. Here's the button:
You can't set the text to be centered in your nib. But you can change the alignment in your code:
- (void)viewDidLoad {
[super viewDidLoad];
self.myButton.titleLabel.textAlignment = UITextAlignmentCenter;
}
I know this is an old question, but I came across it in my own attempt to center the multi-line text of a UIButton in IB. What I found is that by default, when "title" is set to "plain" and "line break" is set to "word wrap" the longest line of the title text is centered and the other lines are left justified to this line (similar to the OP's screen capture).
In order to have all the lines centered properly, "title" needs to be changed to "attributed." This provides many more options to customize the appearance of the title text. Center each of the lines of text (you can now actually change the alignment for each line individually). Also be sure to set "line breaking" to "word wrap" under "more..." above the text. There seems to be a bug with how this line breaking option behaves, at least in Xcode 4.5 at this time, because the text on the button in IB will look incorrect, truncating everything except the first line. It seems the "word wrap" and truncate options are interpreted backwards in IB, but if you run the app it behaves correctly in the simulator.
Actually you can do it in interface builder.
Just set Title to "Attributed" and then choose center alignment.
#from comments : To wrap you need to set Line Break to Character Wrap or Word Wrap.
P.S : This might not render in xcode. But, it will work at runtime.
You can set the center multiline text in UIButton through storyboard.
This is how you make the text have two or more lines.
Set the below key Path at
Identity Inspector --> User defined runtime attributes --> add new key value pair with below
titleLabel.textAlignment - NSNumber - 1
and
titleLabel.numberOfLines - NSNumber - 5 - or use "0" meaning "any number"
It will look like this:
Note that (2016) unfortunately it does not actually show the two or more lines of text in Storyboard (you see only the first one), but it works perfectly when you run in simulator or device.
For IB set Title to "Attributed" and select center alignment (like Alexander Danilov suggested)
But if you want to do it in code using Swift 4:
// center button text
yourButton.titleLabel?.textAlignment = .center
// enable multiline if needed
yourButton.titleLabel?.numberOfLines = 0
Not all options are done using Interface Builder therefore you must do some of them by coding, and usually we do them inside the function viewDidLoad.
To center your text inside a button by code you can use the following:
button1.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
You can use the same technique to align the text to any direction, for example to the left:
button1.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
But this will take the text too much to the left and you might want to have some space before it keeping the alignment to the left, so you add an inset after the aligning code as follows:
button1.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
In this case we are pushing the text from the Y axis by 10 points. I say here points not pixels because as you know Apple uses the points technique to measure distances to be able to adapt easily between normal display and retina display (where retina is 2 times the normal one).
I haven't tried it out yet, but I think a way to do it might be create a CGRect on top of your button, then use it as a frame, create a label, and then you can play with the label, set the textAlignment property to be UITextAlignmentCenter, and set the background color to be clear.
This works with uitableview but I don't know whether that will work for button. Hope this helps.