macOS 应用开发小集锦

开发小集锦

记录开发中的琐碎技巧,以备查阅.

NumberFormatter

  • 将数字转为文字描述
 let formatter = NumberFormatter()
 formatter.numberStyle = .spellOut
 guard let words = formatter.string(from: 12320) else{ return }
 print("words = \(words)")
//结果: words = 一万二千三百二十

输出结果与当前app的语言环境有关(默认为English),如果需要修改工程的语言环境,需要设置Edit Scheme...

设置工程默认语言环境

weak 与 unowned

swift的闭包中我们在使用self时为了避免循环引用的问题,经常使用weak self 或者unowned self,这两种方式既相似又略有区别

  • [weak self]在闭包中的 self可选类型
HttpTool.request("your-url").complete{ [weak self] response in
 self?.mode = response.data
self?.updateUI()
}
  • [unowned self]在闭包中的self非可选类型: 因此若闭包执行前self被释放,会造成crash
HttpTool.request("your-url").complete{ [unowned self] response in
 self.mode = response.data
self.updateUI()
}
  • 推荐用法:
HttpTool.request("your-url").complete{ [weak self] response in
 guard let strongSelf = self else {return}
 self.mode = response.data
 self.updateUI()
}

NSBezierPath 转 CGPath

extension NSBezierPath {
    public var cgPath: CGPath {
        let path = CGMutablePath()
        var points = [CGPoint](repeating: .zero, count: 3)
        
        for i in 0 ..< self.elementCount {
            let type = self.element(at: i, associatedPoints: &points)
            switch type {
            case .moveToBezierPathElement:
                path.move(to: points[0])
            case .lineToBezierPathElement:
                path.addLine(to: points[0])
            case .curveToBezierPathElement:
                path.addCurve(to: points[2], control1: points[0], control2: points[1])
            case .closePathBezierPathElement:
                path.closeSubpath()
            }
        }
        
        return path
    }
}

获取你的公网IP地址

设置请求header后发送请求到http://ifconfig.me/ip,获取结果即可~(是不是很easy ^ _ ^ )

示例代码:

let headers: HTTPHeaders = [
      "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6"
        ]
Alamofire.request("http://ifconfig.me/ip",headers:headers).responseString { (response) in
            print(response.result.value ?? "error")
        }

字符串插入分隔符与分隔词

var des = "Version"
let startIndex = des.startIndex
des.insert("-", at: des.index(startIndex, offsetBy: 4))    // 插入单个字母符号
des.insert(contentsOf: "hello", at: des.index(startIndex, offsetBy: 5))  // 插入多字符
print(des)  // 结果为Vers-helloion

文本朗读(iOS通用)

语音朗读

NSSpeechSynthesizer类提供了将文字转为语音的功能

 let speach = NSSpeechSynthesizer()
 speach.startSpeaking("Hello world")    // 开始朗读
  • 获取系统支持的朗读声音
for voiceName in NSSpeechSynthesizer.availableVoices {
       print(voiceName)
 }
// 输出结果
// VoiceName(_rawValue: com.apple.speech.synthesis.voice.Alex)
// VoiceName(_rawValue: com.apple.speech.synthesis.voice.alice)
// VoiceName(_rawValue: com.apple.speech.synthesis.voice.alva)
......
  • 设置朗读声音
let speach = NSSpeechSynthesizer()
let result = speach.setVoice(NSSpeechSynthesizer.VoiceName.init(rawValue: "com.apple.speech.synthesis.voice.damayanti"))
  if result {
      print("success")
  }else{
      print("failure")
  }
  • 监听朗读事件
    设置代理
let speach = NSSpeechSynthesizer()
speach.delegate = self

实现相应的代理方法,监听事件回调

func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
        print("will speaKWord \(string) \(characterRange)")
    }
func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakPhoneme phonemeOpcode: Int16) {
        print("will speakPhoneme \(phonemeOpcode)")
    }
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterSyncMessage message: String) {
        print("did encounter message \(message)")
    }
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
        print("did finishedSpeaking ")
    }
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterErrorAt characterIndex: Int, of string: String, message: String) {
        print("did error \(string)")
    }

添加系统提醒事件(iOS通用)

提醒事项

使用EKEventStoreEKReminder实现添加事件到系统的提醒应用

class ViewController: NSViewController {
  fileprivate let eventStore = EKEventStore()    // 建议使用单利模式
  fileprivate  var isAuthor = false     // 检测是否进行授权
  ... ... 
}

extension ViewController{
    fileprivate func remindme(){     
        // 获取系统的授权状态
        let authorStatus = EKEventStore.authorizationStatus(for: EKEntityType.reminder)
       // 判断授权结果     
        switch authorStatus {
        case .authorized:   // 已授权,设置授权结果为true
            isAuthor = true
        case .notDetermined:   // 尚未授权,进行授权请求(回调在子线程,若处理UI,需要切到主线程)
            eventStore.requestAccess(to: .reminder, completion: { [weak self] (granted, error) in
                guard let strongSelf = self else {return}
                if granted {
                    DispatchQueue.main.async {
                        strongSelf.isAuthor = granted
                        strongSelf.createRemind()
                    }
                }
            })
        case .denied,.restricted:  // 用户拒绝授权
            print("user deny ")
        }
        
        if !isAuthor {return}
        createRemind()
    }
    
    // 创建提醒
    fileprivate func createRemind(){
       
        let reminder = EKReminder(eventStore: eventStore)   // 创建提醒器
        reminder.calendar = eventStore.defaultCalendarForNewReminders() // 设置新提醒  
        reminder.title = "remide title"      // 设置提醒事件的标题
        let alarmDate = Date(timeInterval: 15, since: Date())   // 设置提醒时间:本例为15秒后
        let alarm = EKAlarm(absoluteDate: alarmDate)    // 创建提醒
        reminder.addAlarm(alarm)          // 添加提醒到提醒器中
        do {
            try  eventStore.save(reminder, commit: true)       // 添加事件到系统提醒应用
        } catch  {
            print("save failure: \(error.localizedDescription)")
        }
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容