How can we get the whole text from selected text in UITextView? - swift

How can I get the whole text in the blue rectangle, inside UITextView, if user select a portion like this:
My current code is to get only the selected text:
if let range = textView.selectedTextRange {
let selectedText = textView.text(in: range) {
//selectedText
}
}

You have to set your view controller as the text view delegate UITextViewDelegate
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
Implement textViewDidChangeSelection method
func textViewDidChangeSelection(_ textView: UITextView)
In this function you can get the UITextPosition of the start and end of the lines of the selection, then create a new range and retrieve the selected text:
func textViewDidChangeSelection(_ textView: UITextView) {
guard let selectedTextRange = textView.selectedTextRange,
let start = textView.tokenizer
.position(from: selectedTextRange.start, toBoundary: .line, inDirection: .backward),
let end = textView.tokenizer
.position(from: selectedTextRange.end, toBoundary: .line, inDirection: .forward),
let range = textView.textRange(from: start, to: end),
let text = textView.text(in: range)
else { return }
print(text)
}
Add this extension helpers to your project:
extension UITextDirection {
static let forward: Self = .init(rawValue: 0)
static let backward: Self = .init(rawValue: 1)
}

Related

Create a hashtag in the text view swift

I found this question: I'm implementing a hashtag recognizer in UITextView but it never highlights two words with the same beginning?. By doing that I want to check the user's run time input text on the text view and each time user types a #hashtag, It will automatically highlight after adding a comma.
Code:-
import UIKit
class ViewController: UIViewController,UITextViewDelegate {
#IBOutlet weak var txtView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
txtView.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
textView.resolveHashTags()
}
}
extension UITextView {
func resolveHashTags() {
let nsText = NSString(string: self.text)
let words = nsText.components(separatedBy: CharacterSet(charactersIn: "#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted)
let attrString = NSMutableAttributedString()
attrString.setAttributedString(self.attributedText)
for word in words {
if word.count < 3 {
continue
}
if word.hasPrefix("#") {
let matchRange:NSRange = nsText.range(of: word as String)
let stringifiedWord = word.dropFirst()
if let firstChar = stringifiedWord.unicodeScalars.first, NSCharacterSet.decimalDigits.contains(firstChar) {
} else {
attrString.addAttribute(NSAttributedString.Key.link, value: "hash:\(stringifiedWord)", range: matchRange)
}
}
}
self.attributedText = attrString
}
}
Current ScreenShot:-
Question:- Can someone please explain to me how to highlight the text after adding a comma? I've tried to implement by above code but no results yet.
Any help would be greatly appreciated.
Thanks in advance.

How do I underline characters in the comments using Swift

For example, I'm writing a function which I want to show an add method. I wanted to underline the comment above like so.
// This is the comment I want to underline
// Adding a few other things you can do with comments would also be helpful
func helpMeUnderlineThisComment(comment: String) -> String {
for char in comment {
if char == comment {
return char.underlined()
}
}
return nil
}
You can simply do it by making an extension:
extension NSAttributedString {
var underlined: NSAttributedString {
return applying(attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue])
}
func applying(attributes: [NSAttributedString.Key: Any]) -> NSAttributedString {
let copy = NSMutableAttributedString(attributedString: self)
let range = (string as NSString).range(of: string)
copy.addAttributes(attributes, range: range)
return copy
}
}
How to use it?
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.textLabel.text = "Some Text"
self.textLabel.attributedText = textLabel.attributedText?.underlined
}}

Custom Attributed string action in swift

I want to make the attributed string action in swift. I want append my custom variable to table view cell data.
I added colour and line also for that string, now i want to add the link for that added attributed string.
Please find my code with the following.
I add this code into cell for row at Index path.
let value = NSMutableAttributedString(string: " tap here.", attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!]).withTextColor(UIColor.disSatifyclr).withUnderlineColor(UIColor.disSatifyclr).withUnderlineStyle(.styleSingle)
if (cell.desclbl.text?.contains("more about donating, "))! {
let description = descriptions[indexPath.section][indexPath.row]
let attributedString = NSMutableAttributedString(string: description)
attributedString.append(value)
cell.desclbl.attributedText = attributedString
}
Here you can handle action using UITextView instead of UILabel on tap of string
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.attributedText = prepareLink()
textView.tintColor = UIColor.black
textView.isSelectable = true
textView.isEditable = false
textView.delegate = self
}
func prepareLink() -> NSMutableAttributedString {
let formattedString = NSMutableAttributedString(string: " tap here ")
let linkAttribute = [
NSAttributedString.Key.foregroundColor: UIColor.green,
NSAttributedString.Key.underlineColor: UIColor.green,
NSAttributedString.Key.underlineStyle: 1,
NSAttributedString.Key.link: URL(string: "http://www.google.com")!
] as [NSAttributedString.Key : Any]
formattedString.addAttributes(linkAttribute, range: NSMakeRange(0, formattedString.length))
return formattedString
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if (URL.absoluteString == "http://www.google.com") {
// Do Something
}
return true
}
}
Note: If you have to continue with UILabel then you have to implement UITapGesture to handle action.

How to change color for some text in text view?

I made my homework for life cycle of view controllers. I used a tabbar to switch controllers. And when switching controllers, the label of text view to display life-cycle methods.
Now I want to change color text in text view for expression "First Controller", "Second Controller", "Third Controller" so that the colors remain the same regardless of which controller I switch to.
https://youtu.be/Od_bFlaA-kY
How to do it?
import UIKit
class FirstViewController: UIViewController {
#IBOutlet var firstTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
FromCodeToScreen.shared.printMessage(textView: firstTextView, viewController: self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
FromCodeToScreen.shared.printMessage(textView: firstTextView, viewController: self)
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
FromCodeToScreen.shared.printMessage(textView: firstTextView, viewController: self)
}
// And similar code in all of the other lifecycle methods
}
import UIKit
class FromCodeToScreen: NSObject {
static let shared = FromCodeToScreen()
private var arrayForData = [String]()
private override init() {
super.init()
}
func printMessage(textView: UITextView, viewController: UIViewController, function: String = #function) {
arrayForData.append((viewController.title ?? "nil") + " - " + (function))
let string = arrayForData.joined(separator: "\n")
textView.text = string
textViewScrollToBottom(textView)
}
}
Change arrayForData string array to NSAttributedString.
class FromCodeToScreen: NSObject {
static let shared = FromCodeToScreen()
private var arrayForData = [NSAttributedString]()
private override init() {
super.init()
}
func printMessage(textView: UITextView, viewController: UIViewController, function: String = #function) {
let color = viewController.view.backgroundColor ?? .white
let attStr = NSMutableAttributedString(string: viewController.title ?? "nil", attributes: [.foregroundColor : color])
attStr.append(NSAttributedString(string: " - " + function + "\n"))
arrayForData.append(attStr)
textView.attributedText = arrayForData.reduce(into: NSMutableAttributedString()) { $0.append($1) }
textViewScrollToBottom(textView)
}
}
Try This
let text = "This is a colorful attributed string"
let attributedText =
NSMutableAttributedString.getAttributedString(fromString: text)
attributedText.apply(color: .red, subString: "This")
//Apply yellow color on range
attributedText.apply(color: .yellow, onRange: NSMakeRange(5, 4))
For more detail click here: https://github.com/iOSTechHub/AttributedString
Try:
firstTextView.textColor = UIColor.red
Apply this on viewDidLoad() for each ViewController.
Change the color to whatever you want.

Change search field's icon

I try to implement search behavior like in Xcode: if you enter something in search field, icon changes color.
I delegate both searchFieldDidStartSearching and searchFieldDidEndSearching to controller and change the image.
The problem is icon's image changes only when window lose it's focus.
class ViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
func searchFieldDidStartSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSActionTemplate")
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSHomeTemplate")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Thanks in advance for any ideas/suggestions.
Although I don't know the reason, it works:
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
Here is the whole code:
class MyViewController: NSViewController {
private lazy var searchField: NSSearchField = {
let searchField = NSSearchField(string: "")
if let searchButtonCell = searchField.searchButtonCell {
searchButtonCell.setButtonType(.toggle)
let filterImage = #imageLiteral(resourceName: "filter")
searchButtonCell.image = filterImage.tinted(with: .systemGray)
searchButtonCell.alternateImage = filterImage.tinted(with: .systemBlue)
}
searchField.focusRingType = .none
searchField.bezelStyle = .roundedBezel
searchField.delegate = self
return searchField
}()
...
}
extension MyViewController: NSSearchFieldDelegate {
func searchFieldDidStartSearching(_ sender: NSSearchField) {
sender.searchable = true
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
sender.searchable = false
}
}
extension NSSearchField {
var searchButtonCell: NSButtonCell? {
(self.cell as? NSSearchFieldCell)?.searchButtonCell
}
var searchable: Bool {
get {
self.searchButtonCell?.state == .on
}
set {
self.searchButtonCell?.state = newValue ? .on : .off
self.refreshSearchIcon()
}
}
private func refreshSearchIcon() {
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
}
}
extension NSImage {
func tinted(with color: NSColor) -> NSImage? {
guard let image = self.copy() as? NSImage else { return nil }
image.lockFocus()
color.set()
NSRect(origin: NSZeroPoint, size: self.size).fill(using: .sourceAtop)
image.unlockFocus()
image.isTemplate = false
return image
}
}
I was having the same issue. A simple override fixed this issue for me
extension NSSearchField{
open override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
}
As you can see when you click inside the view it's still focussed on the search text field(as you can still type in it after you clicked underneath it). Since the change image is on when it loses focus, you should check if you clicked outside of the text field.
Solve problem by subclassing NSSearchFieldCell and assign this class to field's cell.
You don't even need to subclass NSSearchFieldCell.
When you create your NSSearchField from code, you can do something like this:
if let searchFieldCell = searchField.cell as? NSSearchFieldCell {
let image = NSImage(named: "YourImageName")
searchFieldCell.searchButtonCell?.image = image
searchFieldCell.searchButtonCell?.alternateImage = image // Optionally
}
If you're using storyboards, you can do the same in didSet of your #IBOutlet.