以下所示,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 }