以下所示,view上有一個button,button一半的frame在父類view bounds以外, 按照iOS系統默認的處理邏輯, 若是點擊按鈕上半部分,則按鈕不會響應時間,若是點擊下半部分才行, 要想讓點擊上半部分同樣相應事件,則須要干預事件的傳遞過程,以下代碼所示. 判斷事件發生的point在button上面,則讓button去響應事件便可.python
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let btnPoint = self.convert(point, to: navigationButton) if navigationButton.point(inside: btnPoint, with: event) { return navigationButton }else{ return super.hitTest(point, with: event) } }
項目需求須要清空webView的歷史紀錄,要否則只能使用兩個webView, 按理說應該是一個就能解決的, 用兩個內心有點不爽. 百度兩個多小時找不到可用的方法, 最終在stackoverflow上面找到一種解決方案, 使用webWKWebView的私有方法, 代碼以下所示:ios
webView.backForwardList.perform(Selector(("_removeAllItems")))
//1.把scrollView添加到控制器view let scrollView = UIScrollView() view.addSubview(scrollView) scrollView.snp.updateConstraints { (make) in make.edges.equalToSuperview() } //2.給scrollView添加一個containerView let containerView = UIView() scrollView.addSubview(containerView) containerView.snp.makeConstraints { (make) in make.edges.equalTo(scrollView) make.width.equalTo(scrollView) } //3.全部的子控件都放到containerView裏面, 在最後一個子控件後設置約束 containerView.snp.makeConstraints { (make) in make.bottom.equalTo(goButton.snp.bottom).offset(20) }
extension UIViewController { static func createFromNib() -> Self { let className = "\(type(of: self))".split(separator: ".").first assert(className != nil, "\n\n \(type(of: self)) -> 找不到對應NIB,請檢查nibName是否綁定Class \n\n") return self.init(nibName: String(className!), bundle: Bundle.main) } }
exit(0)
早期的ios版本, 能夠獲取到udid, mac address等信息, 可是ios8以後的版本都獲取不到了, UUID雖然能夠保證惟一性, 可是沒法保證每次獲取到的相同. IDFA也能夠再不少時候保證惟一性, 可是大概ios10以後, 用戶能夠選擇關閉IDFA追蹤, 這時候, 就沒法獲取到正確的IDFA了. 在網上找了很多的解決方案, 其中最靠譜的是應用第一次打開時候生成惟一的一個UUID,並存儲到SSKeyChain中. 而SSKeyChain中的數據能夠保證每次重啓/重裝/應用升級/系統升級都不發生變化. 有說惟一會致使SSKeyChain發生變化的是重置系統和resetchain方法重置KeyChain.web
自定義鍵盤實際上是想當簡單的, 自定義鍵盤主要是用到了UITextView的inoutView屬性, 當給UITextField賦值inputView屬性時候, 若是textField得到輸入焦點, 則會彈出你設置的鍵盤View. 至於你想在鍵盤上畫什麼東西, 那就是你的事情了, 隨便畫, 就是這麼簡單, 沒什麼好說的.shell
let rect = CGRect(x: 0, y: 0, width: 0, height: 200) let keyBoardView = CustomKeyBoardView(frame: rect) textField.inputView = keyBoardView
// let url = URL(string: UIApplicationOpenSettingsURLString) // UIApplication.shared.openURL(url!)
實現自定義鍵盤的另外一種方案是使用Custom Keyboard Extension
, 這裏不作詳細的探究.swift
Swift 中沒有宏的概念,可是提供了 Active Compilation Conditions ,這個設置能夠替代以前預編譯宏的方式,來作本身的條件編譯.閉包
//swift支持的預編譯格式 #if <condition> #elseif <condition> #else #endif //1.檢測系統型號或者系統內核類型 // os()只能檢測系統類型,而沒法檢測系統的版本, OSX, iOS //arch()檢測處理器的型號,x86_64, arm, arm64, i386 #if os(OSX) typealias Color = NSColor #elseif os(iOS) typealias Color = UIColor #endif //2.檢測是Debug版本仍是Release版本 #if DEBUG #elseif Release #else #endif //3.檢測swift語言的版本 #if swift(>=3.2) // Swift 3.2 及以上 #else // Swift 3.2 如下 #endif //4.檢測iOS系統版本, 不一樣的系統執行不一樣的代碼 if #available(iOS 9.0, *) { // iOS 9 及以上 }else{ // iOS 9 如下 }
swift中也能夠自定義編譯類型, 並在本身的代碼中使用本身的類型, 能夠再以下所示的位置設置自定義類型.app
Xcode也給咱們提供了三種實用的簡易標記,即 MARK、TODO、FIXME,如今這些在 Objective-C 或者 Swift 環境下都是可使用的。須要注意的是 MARK、TODO、FIXME 均必須大寫,Xcode將會在代碼中尋找這樣的註釋,而後以粗體標籤的形式將名稱顯示在導航欄,就如同咱們會用 「#pragma mark -」 符號來標記代碼區間同樣的道理。async
TODO, MARK, FIXME的使用方法以下所示:ide
//TODO: 標記未來要完成的內容 //MARK: 標記一件事情 //FIXME: 標記之後要修正或完善的內容 //ERROR:標記一段錯誤
然而, 問題來了, 雖然可以標記, 可是若是不是警告⚠️或者錯誤❌, 程序員們常常會忘記本身標記的這些東西. 經常會發現本身幾個月前標記的問題, 結果拖了幾個月都記不起來. 那麼該怎麼解決呢? 答案就是在run script build phases
添加一段編譯腳本.
1.切換到:target-->build phases-->editor-->add run script build phases
;
2.選擇:New Run Script Parse
;
3.添加以下shell腳本:;
TAGS="TODO:|FIXME:|WARNING:" ERRORTAG="\/\/ERROR:|\/\/ ERROR:" find "${SRCROOT}" \( -name "*.h" -or -name "*.m" -or -name "*.swift" \) -and -type f -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($TAGS).*\$|($ERRORTAG).*\$" | perl -p -e "s/($TAGS)/ warning: \$1/"| perl -p -e "s/($ERRORTAG)/ error: \$1/"
Prefect!
//tip1: we can combine protocols and subtypes with `&`, ex: protocol A {} class C {} class D: C, A {} func someFunc(using: A & C){} someFunc(using: D()) //tip2: Protocol extensions can provide default property values protocol Fadeable { var fadeSpeed: TimeInterval {get} func fadeOut() } extension Fadeable where Self: UIView { var fadeSpeed: TimeInterval {return 0.25} func fadeOut() { UIView.animate(withDuration: fadeSpeed) { self.alpha = 0 } } } class CustomView: UIView, Fadeable {} //tip3: Use destructing to manipulate tuple // 元組能夠當成參數返回值被直接返回; 元祖能夠經過"="直接解構到變量上; 甚至能夠用來交換兩個變量 func getGredentials() -> (String, String) {return ("swift", "python")} let someStrs = getGredentials() //直接使用一個變量接收 var (str1, str2) = getGredentials() //使用元祖接收 var (someStr1, someStr2) = someStrs //直接使用兩個變量組成的元組解構另外一個元組變量 (str2, str1) = (str1, str2) //交換兩個變量的值 //tip4: public getter, private setter // `public private(set)`, This lets us mark a property as being open for reading but closed for writing struct Bank { public private(set) var address: String } //tip5: 定義本身的init可是不覆蓋系統提供的初始化方法 //在結構體中, 若是提供本身的init方法, 則系統再也不提供, 這是爲了防止你在你本身的初始化方法中作了重要的操做, 而 // 這時候若是系統繼續提供初始化方法, 則會忽略你的重要初始化.若是你想要在本身提供初始化方法時候保留系統提供的 // 初始化方法, 則能夠在extension中提供初始化方法. //在class中, 一樣的道理, 你在extension中提供的init方法不會覆蓋掉系統提供的init方法. 和結構體不一樣的是, 類 // 擴展中的init方法須要使用`convenience`聲明. //tip6: static和class的區別 // static和class都可以用來聲明類變量和類方法, 他們的區別在於繼承上. class聲明的變量和方法能夠被子類繼承, 而static // 聲明的不可以被繼承. //tip7: `==`和`===`的區別 // ==表示比較兩個值是否相等 // ===表示比較兩個變量的內存地址是否相等
Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: <#T##(Timer) -> Void#>)
這樣的方法,能夠更加方便的建立和管理Timer.asyncAfter
會不會更好一點呢?defaultRunLoopMode
中, 若是用戶正在滑動一個tabbleView, 那麼你的定時器將不會被觸發. 若是你想在用戶操做UI的同時定時器也在跑, 則你須要將timer添加到commonModes
中.CADisplayLink
.//使用timer的userinfo. 給timer附加一些數據, Dict類型的數據, 能夠放在參數context中, 以下所示: let context = ["user": "@twostraws"] Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: context, repeats: true) //在timer對象中能夠取出timer.userinfo,以下所示 @objc func fireTimer(timer: Timer) { guard let context = timer.userInfo as? [String: String] else { return } let user = context["user", default: "Anonymous"] print("Timer fired by \(user)!") runCount += 1 if runCount == 3 { timer.invalidate() } } //使用commonModes let context = ["user": "@twostraws"] let timer = Timer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: context, repeats: true) RunLoop.current.add(timer, forMode: .commonModes)
摘自: https://www.hackingwithswift.com/articles/117/the-ultimate-guide-to-timer
詳情參考: https://www.hackingwithswift.com/articles/118/uiactivityviewcontroller-by-example
demo參考測試代碼庫.
1. 建立自定義的UIApplication子類MyApplication; 2. 建立main.swift文件; 3. 在自定義的main.swift中指定初始化的MyApplication; let _ = UIApplicationMain( CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv) .bindMemory( to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc) ), NSStringFromClass(WHApplication.self), NSStringFromClass(AppDelegate.self) ) 4.刪除原來Appdelegate中的`@UIApplicationMain`註解. Done.
隱士可選值是指那些不管什麼時候使用它們都會自動強制解包的可選值. 目前來看, 在項目中形成隱士可選值的方式有兩種:
var str: String!
, 這種狀況下, 若是直接使用str, 則會形成程序崩潰. 在實際開發中, 要避免這種寫法.雖然隱士可選值在行爲上和非可選值同樣, 可是咱們依然能夠對它們使用可選鏈, nil合併, if let和map, 全部的操做都和可選值同樣.
func synchronized(lock: AnyObject, closure: () -> ()) { objc_sync_enter(lock) closure() objc_sync_exit(lock) }
使用dispatch_semaphore
建立信號, 使用信號量來控制訪問線程的數量.
/// 判斷字體是否存在 private static func isFontLoaded(fontName: String) -> Bool { let tmpFont = UIFont(name: fontName, size: 10) return tmpFont?.fontName == fontName } /// 動態加載字體 private static func dynamicLoadFont(fontName: String) { guard var fontPath = Bundle.main.path(forResource: "UIFoundationKit.bundle", ofType: nil) else { return } fontPath = "\(fontPath)/\(fontName.uppercased()).OTF" let url = URL(fileURLWithPath: fontPath) if let fontData = try? Data(contentsOf: url), let provider = CGDataProvider(data: fontData as CFData), let font = CGFont.init(provider) { CTFontManagerRegisterGraphicsFont(font, nil) } }
@convention
@convention
是用來修飾閉包的, 用來代表此閉包能夠兼容什麼語言格式的閉包. 好比convention(c)
表示此閉包能夠傳遞到c函數中. 以下所示:
使用示例:
let saySomething_c : @convention(c) (String)->Void = { print("i said: \($0)") } let saySomething_oc : @convention(block) (String)->Void = { print("i said: \($0)") } let saySomething_swift : @convention(swift) (String)->Void = { print("i said: \($0)") }
let obj = Obj() /// 方案一: 測試中 發現做用在<引用類型>的對象上能確保正確性 let point = Unmanaged<AnyObject>.passUnretained(obj as AnyObject).toOpaque() let hashValue = point.hashValue // 這個就是惟一的,能夠做比較 /// 方案二:測試中 發現做用在<值類型>的對象上能確保正確性 let hashValue2 = withUnsafePointer(to: &obj) { (point) -> Int in /// 閉包的實現有多種,可根據本身需求修改 return point.hashValue }