swift 处理文本中的链接

handle-links-in-text
//Version 1, extension
extension NSMutableAttributedString {
    
    public func setAsLink(textToFind: String, stringURL: String) -> Bool {
        let foundRange = mutableString.range(of: textToFind)
        
        if foundRange.location != NSNotFound {
            addAttribute(.link, value: stringURL, range: foundRange)
            return true
        }
        
        return false
    }
    
}


//Version 2, with ranges.
class ViewController: UIViewController {
    //Disable "editable" for textView.
    @IBOutlet weak var textView: UITextView! 
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let attributedString = NSMutableAttributedString(string: "Want to play the best game ever? Try Kingdom Hearts!")
        attributedString.addAttribute(.link, value: "https://en.wikipedia.org/wiki/Kingdom_Hearts", range: NSRange(location: 37, length: 14))
        textView.attributedText = attributedString
    }

}

extension ViewController: UITextViewDelegate {
    
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        UIApplication.shared.open(URL)
        return false
    }
    
}

swift 使用AVSpeechSynthesizer读取文本

reading-text
import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    private let textToRead = """
    Bloodborne is an action role-playing game developed by FromSoftware and published by Sony Computer Entertainment for the PlayStation 4, which released worldwide in March 2015. Bloodborne follows the player character, the Hunter, through the decrepit Gothic, Victorian era-inspired city of Yharnam, whose inhabitants have been afflicted with an abnormal blood-borne disease, with the player character unraveling the city's intriguing mysteries while fighting beasts, ultimately attempting to find the source of the plague and stop it.

The game is played from a third-person perspective, players control a customizable protagonist, and gameplay is focused on weapons-based combat and exploration. Players battle varied enemies, including bosses, using items such as swords and firearms, and journey through the story, exploring the game's different locations, interacting with non-player characters, collecting key items involved in the story, and discovering and unraveling the world's many mysteries. Bloodborne began development in 2012 under the working title of Project Beast. Bearing many similarities to the Souls series of games by the same developer and director, Bloodborne was partially inspired by the literary works of authors H. P. Lovecraft and Bram Stoker, and the architectural design of certain real world locations in places such as Romania and the Czech Republic. The decision by game director Hidetaka Miyazaki to create a new intellectual property (IP) and not another Souls game was made because he wanted to create something "different"; at the same time, Sony wanted a new IP to be made exclusively for the PlayStation 4.
"""

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func readText(_ sender: Any) {
        try? AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
        
        let utterance = AVSpeechUtterance(string: textToRead)
        utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
        
        let synthesizer = AVSpeechSynthesizer()
        synthesizer.delegate = self
        
        setOutput(for: synthesizer, to: getAudioChannelNames())
        
        synthesizer.speak(utterance)
    }
    
    private func getAudioChannelNames() -> [String] {
        let route = AVAudioSession.sharedInstance().currentRoute
        let outputPorts = route.outputs
        var channels = [String]()
        for port in outputPorts {
            guard let portChannels = port.channels else {
                fatalError()
            }
            
            for channel in portChannels {
                channels.append(channel.channelName)
            }
        }
        
        return channels
    }
    
    private func setOutput(for speechSynthesizer: AVSpeechSynthesizer, to channelNames: [String]) {
        let route = AVAudioSession.sharedInstance().currentRoute
        let outputPorts = route.outputs
        var channels = [AVAudioSessionChannelDescription]()
        for channelName in channelNames { //1
            for outputPort in outputPorts { //2
                guard let portChannels = outputPort.channels else { //3
                    fatalError()
                }
                
                for channel in portChannels {
                    if channel.channelName == channelName {
                        channels.append(channel)
                    }
                }
            }
        }
        
        if channels.count > 0 {
            speechSynthesizer.outputChannels = channels
        }
    }
}

swift 获取当前的WiFi AP信息

获取当前的WiFi AP信息

README.md
> http://www.yuukinishiyama.com/2018/10/23/wifi-ap-info-swift4/

> 需要開發者帳號

![](https://raw.githubusercontent.com/WingCH/Image/master/20190623171807.png)

會自動生成呢個 or 可能出現係info.plist

![](https://raw.githubusercontent.com/WingCH/Image/master/20190623171829.png)

如果無整既話以下既code會出nil

```swift
import UIKit
import SystemConfiguration.CaptiveNetwork

struct NetworkInfo {
    public let interface:String
    public let ssid:String
    public let bssid:String
    init(_ interface:String, _ ssid:String,_ bssid:String) {
        self.interface = interface
        self.ssid = ssid
        self.bssid = bssid
    }
}

class NetworkInfos: NSObject {

    func getNetworkInfos() -> Array<NetworkInfo> {
        // https://forums.developer.apple.com/thread/50302
        guard let interfaceNames = CNCopySupportedInterfaces() as? [String] else {
            print("no interfaceNames")
            return []
        }
        
        let networkInfos:[NetworkInfo] = interfaceNames.compactMap{ name in
            print(name)
            guard let info = CNCopyCurrentNetworkInfo(name as CFString) as? [String:AnyObject] else {
                print("no info")
                return nil
            }
            guard let ssid = info[kCNNetworkInfoKeySSID as String] as? String else {
                print("no ssid")
                return nil
            }
            guard let bssid = info[kCNNetworkInfoKeyBSSID as String] as? String else {
                print("no bssid")
                return nil
            }
            return NetworkInfo(name, ssid,bssid)
        }
        return networkInfos
    }
}
```

```
//use
print(NetworkInfos.init().getNetworkInfos())

//result
[ou_fyp.NetworkInfo(interface: "en0", ssid: "OUHK-Student", bssid: "34:fc:b9:6c:41:d4")]
```
empty.swift
//Empty

swift 带有UIImage标题的导航栏

带有UIImage标题的导航栏

README.md
> https://stackoverflow.com/a/54526843/10999568

![](https://raw.githubusercontent.com/WingCH/Image/master/20190623171549.png)

```swift
override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.titleView = navTitleWithImageAndText(titleText: "Dean Stanley", imageName: "location")
    }


func navTitleWithImageAndText(titleText: String, imageName: String) -> UIView {
    
    // Creates a new UIView
    let titleView = UIView()
    
    // Creates a new text label
    let label = UILabel()
    label.text = titleText
    label.sizeToFit()
    label.center = titleView.center
    label.textAlignment = NSTextAlignment.center
    
    // Creates the image view
    let image = UIImageView()
    image.image = UIImage(named: imageName)
    
    // Maintains the image's aspect ratio:
    let imageAspect = image.image!.size.width / image.image!.size.height
    
    // Sets the image frame so that it's immediately before the text:
    let imageX = label.frame.origin.x - label.frame.size.height * imageAspect
    let imageY = label.frame.origin.y
    
    let imageWidth = label.frame.size.height * imageAspect
    let imageHeight = label.frame.size.height
    
    image.frame = CGRect(x: imageX, y: imageY, width: imageWidth, height: imageHeight)
    
    image.contentMode = UIView.ContentMode.scaleAspectFit
    
    // Adds both the label and image view to the titleView
    titleView.addSubview(label)
    titleView.addSubview(image)
    
    // Sets the titleView frame to fit within the UINavigation Title
    titleView.sizeToFit()
    
    return titleView
    
}
extension ViewController: NetworkStatusListener {
    
    func networkStatusDidChange(status: Reachability.Connection) {
        
        switch status {
        case .none:
            debugPrint("ViewController: Network became unreachable")
        case .wifi:
            networkStatus.text = "wifi"
            debugPrint("ViewController: Network reachable through WiFi")
        case .cellular:
            networkStatus.text = "cellular"
            debugPrint("ViewController: Network reachable through Cellular Data")
        }
    }
}
```
empt.swift
//Empty

swift 可编码

可编码

README.md
json格式如下
```json
{
    "AP name": "AP888",
    "Band": "2.4G",
    "Block": "C",
    "Floor": "15",
    "Location": "C09-ST1",
    "SSID": "OUHK-Staff"
}
```

我使用了[Paste JSON as Code • quicktype](https://itunes.apple.com/hk/app/paste-json-as-code-quicktype/id1330801220?mt=12)
![IMAGE](quiver-image-url/067C2AC04741309C9A807C89DDE4914A.jpg =1392x863)

```swift
do{
  let decoder = JSONDecoder()
  let wifiInfo = try decoder.decode(WiFi.self, from: (data!))
  print(wifiInfo.location) 
}catch let error{
  print(error)
}
```
> Result : C09-ST1

-----

## to json

```swift
import UIKit
import Firebase

class User:Codable {
    
    let email: String
    var studentId: String?
    let name: String
    var persistedFaceIds: [String:String]?
    var personId: String?
    
    init() {
        self.email = (Auth.auth().currentUser?.email)!
        self.name = (Auth.auth().currentUser?.displayName)!
        self.persistedFaceIds = [:]
        self.studentId = ""
        self.personId = ""
    }
    
    func getJson() -> String {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        let data = try! encoder.encode(self)// self = 當前user
        let encodedString = String(data: data, encoding: .utf8)!
        return encodedString
    }
    
    func getData() -> Data {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        let data = try! encoder.encode(self)
        return data
    }
    
}

```
empty.swift
//Empty

swift 纸板

纸板

pasteboard.swift
if let theString = pasteboardString {
            print("String is \(theString)")
}

swift Swift提取正则表达式匹配

Swift提取正则表达式匹配

README.md
> https://stackoverflow.com/a/40040472/10999568

```swift
extension String {
    func matchingStrings(regex: String) -> [[String]] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
        let nsString = self as NSString
        let results  = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
        return results.map { result in
            (0..<result.numberOfRanges).map {
                result.range(at: $0).location != NSNotFound
                    ? nsString.substring(with: result.range(at: $0))
                    : ""
            }
        }
    }
}
```



## 大眾點評分享字串:
蒸的好海鲜馆,★★★★☆,¥246/人,蛇口海鲜,南海大道蛇口网谷万融大厦C座G层101http://m.dianping.com/appshare/shop/23995892

```swift
var regexed = theString.matchingStrings(regex:"^([^,]+),([^,]+),([^,]+),([^,]+),([^http]*)(.*)")
print(regexed)
```

**[["蒸的好海鲜馆,★★★★☆,¥246/人,蛇口海鲜,南海大道蛇口网谷万融大厦C座G层101http://m.dianping.com/appshare/shop/23995892", "蒸的好海鲜馆", "★★★★☆", "¥246/人", "蛇口海鲜", "南海大道蛇口网谷万融大厦C座G层101", "http://m.dianping.com/appshare/shop/23995892"]]**

```swift
if !regexed.isEmpty{
    print("Name: \(regexed[0][1])")
    print("Address: \(regexed[0][5])")
    print("Url: \(regexed[0][6])")
}else{
    print("regexed is Empty")
}

```

**Name: 蒸的好海鲜馆**

**Address: 南海大道蛇口网谷万融大厦C座G层101**

**Url: http://m.dianping.com/appshare/shop/23995892**
empty.swift
//Empty

swift 收集视图的HGPlaceholders

Collection view place holder

  let provider = PlaceholdersProvider.default
        provider.add(placeholders: .noRecordsYet)
        collectionView.placeholdersProvider = provider
        
        
        
  extension Placeholder {
    static var noRecordsYet: Placeholder {
        var data = PlaceholderData()
        data.title = Localizations.InWorkout.ProtocolSection.NoRecordsYet.Title
        data.subtitle = Localizations.InWorkout.ProtocolSection.NoRecordsYet.SubTitle
        data.image = #imageLiteral(resourceName: "noRecordsIcon")

        var style = PlaceholderStyle()
        style.backgroundColor = .background
        style.isAnimated = true

        let placeholder = Placeholder(data: data, style: style, key: PlaceholderKey.noResultsKey)
        return placeholder
    }
}

swift ClassNameUtil

ClassNameUtil
import Foundation

class ClassNameUtil {
    
    class func classNameAsString(for object: Any) -> String {
        return String(describing: type(of: object))
    }
    
}

swift Parallellogram UIView

TiltedView
class TiltedView: UIView {
    
    init() {
        super.init(frame: .zero)
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func draw(_ rect: CGRect) {
        let path = UIBezierPath()
        
        path.move(to: CGPoint(x: bounds.minX, y: bounds.minY + BrandBackgroundOffset.Offset)) //top left corner
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY)) //top right corner
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) //bottom right corner
        path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY)) //bottom left corner
        
        // Close the path. This will create the last line automatically.
        path.close()
        UIColor.fr_darkIndigo.setFill()
        path.fill()
    }
    
}