How do I underline characters in the comments using Swift - 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
}}

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.

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.

I don't know what I'm doing wrong in this Swift project?

I'm trying to program a formal system that has these rules:
You start with the string "MI" (which is the text of the label at the beginning)
There are four rules of inference :
1) If the last character of the string is "I", you can add a "U" to the string (ex: MI --> MIU)
2) Being x a sequence of letters, Mx can be replaced with Mxx (ex: MIU --> MIUIU)
3) If the string contains "III", you can replace "III" with "U" (ex: MUIII --> MUU)
4) If the string contains "UU", you can delete the "UU" part (ex: MIIUU --> MII)
In my project, rule 1 corresponds to button1, rule 2 to button2, rule 3 to button3, rule 4 to button 4
Also, since a user may end up in a infinite loop sometimes (ex: if you get to "MIU", from then on you can only use rule 2 and get "MIUIU" and so on) I added a "clear" button.
I want the clear button to just turn the Label.text into "MI" so the user can just go back to the starting point and retry
The point is, for some reason when I press the "clear" button the Label.text does not turn into "MI" as I would want it to, but it turns into "MIU" instead!
What am I missing?
PS: I know this might is probably something extremely easy that I'm stuck on, but I don't have much programming experience
This is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var rule1Button: UIButton!
#IBOutlet weak var rule2Button: UIButton!
#IBOutlet weak var rule3Button: UIButton!
#IBOutlet weak var rule4Button: UIButton!
#IBOutlet weak var clearButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
clear()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var string : String = "MI"
var string1 : String = ""
func rule1() {
if string.characters.last == "I" {
string.append("U")
Label.text = string
}
}
func rule2() {
string1 = string.replacingOccurrences(of: "M", with: "")
string = string + string1
Label.text = string
}
func rule3() {
string = self.string.replacingOccurrences(of: "III", with: "U")
Label.text = string
}
func rule4() {
string = self.string.replacingOccurrences(of: "UU", with: "")
Label.text = string
}
func clear() {
string = "MI"
Label.text = string
}
#IBAction func rule1Button(_ sender: Any) {
rule1()
}
#IBAction func rul2Button(_ sender: Any) {
rule2()
}
#IBAction func rule3Button(_ sender: Any) {
rule3()
}
#IBAction func rule4Button(_ sender: Any) {
rule4()
}
#IBAction func clearButton(_ sender: Any) {
clear()
}
}
Code is just fine - you surely have wrong connection in your storyboard between #IBActions and Buttons.
Reconnect all of them and you should be fine.
If you right click on your UIButton in Storyboard, there is information to which IBAction it connects.
Check all buttons and fix broken ones.
Aside from that your code can be simplified a lot, and I encourage you to use didSet to keep your variable value with UILabel text.
var string : String = "" { //no need for default value here, as you are calling clear() on viewDidLoad(), better keep constants in one place
didSet {
Label.text = string
}
}
func rule1() {
if string.characters.last == "I" {
string = string + "U"
}
}
func rule2() {
string = string + string.replacingOccurrences(of: "M", with: "")
}
func rule3() {
string = self.string.replacingOccurrences(of: "III", with: "U")
}
func rule4() {
string = self.string.replacingOccurrences(of: "UU", with: "")
}
func clear() {
string = "MI"
}

Simple clickable link in Cocoa and Swift

Im writing a desktop app in swift for mac 10.11 and I would like to add a link to the about page.
Very much like this: https://developer.apple.com/library/mac/qa/qa1487/_index.html
I haven't been able to find a good tutorial or reference.
Any help would be much appreciated
Swift 4, xCode 9
#IBDesignable
class HyperlinkTextField: NSTextField {
#IBInspectable var href: String = ""
override func resetCursorRects() {
discardCursorRects()
addCursorRect(self.bounds, cursor: NSCursor.pointingHand)
}
override func awakeFromNib() {
super.awakeFromNib()
// TODO: Fix this and get the hover click to work.
let attributes: [NSAttributedStringKey: Any] = [
NSAttributedStringKey.foregroundColor: NSColor.linkColor,
NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue as AnyObject
]
attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
}
override func mouseDown(with theEvent: NSEvent) {
if let localHref = URL(string: href) {
NSWorkspace.shared.open(localHref)
}
}
}
Modified the existing answers to allow for a substring of the label's text to become underlined and blue, so you can do something like: This is the answer
// A text field that can contain a hyperlink within a range of characters in the text.
#IBDesignable
public class SubstringLinkedTextField: NSTextField {
// the URL that will be opened when the link is clicked.
public var link: String = ""
#available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'link' instead.")
#IBInspectable public var HREF: String {
get {
return self.link
}
set {
self.link = newValue
self.needsDisplay = true
}
}
// the substring within the field's text that will become an underlined link. if empty or no match found, the entire text will become the link.
public var linkText: String = ""
#available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'linkText' instead.")
#IBInspectable public var LinkText: String {
get {
return self.linkText
}
set {
self.linkText = newValue
self.needsDisplay = true
}
}
override public func awakeFromNib() {
super.awakeFromNib()
self.allowsEditingTextAttributes = true
self.isSelectable = true
let url = URL(string: self.link)
let attributes: [NSAttributedStringKey: AnyObject] = [
NSAttributedStringKey(rawValue: NSAttributedStringKey.link.rawValue): url as AnyObject
]
let attributedStr = NSMutableAttributedString(string: self.stringValue)
if self.linkText.count > 0 {
if let range = self.stringValue.indexOf(substring: self.linkText) {
attributedStr.setAttributes(attributes, range: range)
} else {
attributedStr.setAttributes(attributes, range: NSMakeRange(0, self.stringValue.count))
}
} else {
attributedStr.setAttributes(attributes, range: NSMakeRange(0, self.stringValue.count))
}
self.attributedStringValue = attributedStr
}
}
The easiest way is to subclass NSTextField to create a HyperlinkTextField. Below is an example:
First, let's add a HyperlinkTextField class to your project:
// HyperlinkTextField.swift
import Cocoa
#IBDesignable
class HyperlinkTextField: NSTextField {
#IBInspectable var href: String = ""
override func awakeFromNib() {
super.awakeFromNib()
let attributes: [String: AnyObject] = [
NSForegroundColorAttributeName: NSColor.blueColor(),
NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
]
self.attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
}
override func mouseDown(theEvent: NSEvent) {
NSWorkspace.sharedWorkspace().openURL(NSURL(string: self.href)!)
}
}
Next, in Interface Builder, drag a label from the Object library to your window.
Select that label, go to the menu View > Utilities > Show Identity Inspector (or press Cmd + Opt + 3) and change the class to HyperlinkTextField
Go to the Attributes Inspector (Cmd + Opt + 4) and set Href to the URL you want to visit.
The label shows black text in Interface Builder but everything will be fine when you run your app. Clicking on the label will open the link in your default browser.
One thing I couldn't achieve was to make the HyperlinkTextField shows up as blue and underlined in Interface Builder. Comments on how to do that are welcome.
Exactly what I needed. Here is the Swift3 version:
import Cocoa
#IBDesignable
class HyperTextField: NSTextField {
#IBInspectable var href: String = ""
override func awakeFromNib() {
super.awakeFromNib()
let attributes: [String: AnyObject] = [
NSForegroundColorAttributeName: NSColor.blue,
NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue as AnyObject
]
self.attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
}
override func mouseDown(with event: NSEvent) {
NSWorkspace.shared().open(URL(string: self.href)!)
}
}
let link = NSTextField()
link.isBezeled = false
link.drawsBackground = false
link.isEditable = false
link.isSelectable = true
link.allowsEditingTextAttributes = true
let url = URL(string: "http://www.google.com")
let linkTextAttributes: [NSAttributedStringKey: Any] = [
NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue,
NSAttributedStringKey.foregroundColor: NSColor.blue,
NSAttributedStringKey.link: url as Any
]
let string = "whatever"
link.attributedStringValue = NSAttributedString(string: string, attributes: linkTextAttributes)
window.contentView?.addSubview(link)
swift 4.2
class HyperlinkTextField: NSTextField {
private var detector: NSDataDetector!
override func awakeFromNib() {
super.awakeFromNib()
detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
}
func setText(text: String ){
let matches = detector.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
for match in matches {
guard let range = Range(match.range, in: text) else { continue }
let url = text[range]
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(.link, value: url, range: match.range)
self.attributedStringValue = attributedString
}
}
}

Changing the view color when comparing values

I created a view to use as background and I would like to change its color when label text is greater or less than variable number. The script is okay but the color is not changing.
Thanks in advance.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var localName: UITextField!
#IBOutlet weak var localNameLabel: UILabel!
#IBOutlet weak var localTemp: UILabel!
#IBAction func getData(sender: AnyObject) {
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=" + localName.text! + "")
}
#IBOutlet weak var fundo: UIView!
override func viewDidLoad() {
super.viewDidLoad()
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=London")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getWeatherData(urlString: String){
let url = NSURL (string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data!)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options:NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(json)
//localNameLabel.text = json[("name")] as? String
if let name = json[("name")] as? String {
localNameLabel.text = name
}
if let main = json[("main")] as? NSDictionary {
if let temp = main[("temp")] as? Double {
//convert kelvin to celsius
let ft = (temp - 273.15)
let myString = ft.description
localTemp.text = myString
self.changeColor()
}
}
} catch let error as NSError {
print(error)
}
var number : Float
func changeColor(){
number = 19.0
if(Float(localTemp.text!) < number){
fundo.backgroundColor = .blueColor()
}else{
fundo.backgroundColor = .orangeColor()
}
}
}
}
Edited to post the entire script
In your view controller you need to add UITextFieldDelegate which will allow you to access methods related to your text field. The top of your view controller should look like this:
class ViewController: UIViewController,UITextFieldDelegate //set delegate to class
You then need to set the delegate of your text field to self in viewDidLoad and add a target for when the text field changes:
override func viewDidLoad() {
super.viewDidLoad()
localTemp.delegate = self //set delegate to this vc
localTemp.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
You can then implement this method which will run on every key press and you need to call your changeColor() method as above:
func textFieldDidChange(textField: UITextField) {
self.changeColor()
}