1.重写hitTest方法,干预iOS事件传递过程

如下所示,view上有一个button,button一半的frame在父类view bounds之外, 按照iOS系统默认的处理逻辑, 如果点击按钮上半部分,则按钮不会响应时间,如果点击下半部分才行, 要想让点击上半部分一样相应事件,则需要干预事件的传递过程,如下代码所示. 判断事件发生的point在button上面,则让button去响应事件即可.

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)
}
}

2.清空WKWebView的历史纪录

项目需求需要清空webView的历史纪录,要不然只能使用两个webView, 按理说应该是一个就能解决的, 用两个心里有点不爽. 百度两个多小时找不到可用的方法, 最终在stackoverflow上面找到一种解决方案, 使用webWKWebView的私有方法, 代码如下所示:

answer in stackoverflow

webView.backForwardList.perform(Selector(("_removeAllItems")))

3.UIScrollView添加约束

        //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)
}

4.swift中从nib创建一个控制器

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)
}
}

5.代码结束应用进程: exit(0)

6.获取应用的唯一标识

早期的ios版本, 可以获取到udid, mac address等信息, 但是ios8之后的版本都获取不到了, UUID虽然可以保证唯一性, 但是无法保证每次获取到的相同. IDFA也可以再很多时候保证唯一性, 但是大概ios10之后, 用户可以选择关闭IDFA追踪, 这时候, 就无法获取到正确的IDFA了. 在网上找了不少的解决方案, 其中最靠谱的是应用第一次打开时候生成唯一的一个UUID,并存储到SSKeyChain中. 而SSKeyChain中的数据可以保证每次重启/重装/应用升级/系统升级都不发生变化. 有说唯一会导致SSKeyChain发生变化的是重置系统和resetchain方法重置KeyChain.

7.自定义键盘

自定义键盘其实是想当简单的, 自定义键盘主要是用到了UITextView的inoutView属性, 当给UITextField赋值inputView属性时候, 如果textField获得输入焦点, 则会弹出你设置的键盘View. 至于你想在键盘上画什么东西, 那就是你的事情了, 随便画, 就是这么简单, 没什么好说的.

let rect = CGRect(x: 0, y: 0, width: 0, height: 200)
let keyBoardView = CustomKeyBoardView(frame: rect)
textField.inputView = keyBoardView

8.打开应用的权限设置页面

// let url = URL(string: UIApplicationOpenSettingsURLString)
// UIApplication.shared.openURL(url!)

实现自定义键盘的另一种方案是使用Custom Keyboard Extension, 这里不做详细的探究.

9.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中也可以自定义编译类型, 并在自己的代码中使用自己的类型, 可以再如下所示的位置设置自定义类型.

10.Swift注释中的TODO & FIXME & ERROR

Xcode也给我们提供了三种实用的简易标记,即 MARK、TODO、FIXME,现在这些在 Objective-C 或者 Swift 环境下都是可以使用的。需要注意的是 MARK、TODO、FIXME 均必须大写,Xcode将会在代码中寻找这样的注释,然后以粗体标签的形式将名称显示在导航栏,就如同我们会用 “#pragma mark -” 符号来标记代码区间一样的道理。

TODO, MARK, FIXME的使用方法如下所示:

//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!

11. 一些关于swift的tips

//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: `==`和`===`的区别
// ==表示比较两个值是否相等
// ===表示比较两个变量的内存地址是否相等

12.timer

  1. ios10之后, iOS提供了Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: <#T##(Timer) -> Void#>)这样的方法,可以更加方便的创建和管理Timer.
  2. 使用Timer也可以完成一些只重复一次的动作, 但是使用GCD的asyncAfter会不会更好一点呢?
  3. 给tiemr附加一些数据. 使用timer的userinfo.
  4. 添加一些tolerance. 给timer添加tolerance可以减少电量消耗, 这可能导致你的timer执行不精确,比如每秒重复触发一次的timer, 可以设置tolerance=0.2s(默认情况下tolerance=0,但是系统肯定会默认添加一个很小的数字), 这样tiemr最多可能会延迟0.2s, 但是一定不会提前, 如上所述例子, 第一次timer可能是1..<1.2s内触发,第二次可能2..<2.2触发,第三次可能3..❤️.2触发. 就是说如果第一次延迟, 第二次的触发时间并不会在第一次的基础之上延迟. That is all!
  5. 把timer添加到RRunloop中. 默认情况下timer会被添加到defaultRunLoopMode中, 如果用户正在滑动一个tabbleView, 那么你的定时器将不会被触发. 如果你想在用户操作UI的同时定时器也在跑, 则你需要将timer添加到commonModes中.
  6. 如果你想让你的定时器精度非常高, 比如60次/秒, 则此时timer是无法满足需求的, timer只能用在精度相对比较低的场景, 如果你有经度比较高的需求, 你可以使用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

13.UIActivityViewController,自定义分享的activityAction

详情参考: https://www.hackingwithswift.com/articles/118/uiactivityviewcontroller-by-example

demo参考测试代码库.

14.swift4.0之后,App中自定义UIApplication

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.

15. swift中的隐士解包

隐士可选值是指那些无论何时使用它们都会自动强制解包的可选值. 目前来看, 在项目中造成隐士可选值的方式有两种:

  1. 暂时来说, 我们在项目中可能会用到Objective-C方法的返回值或者代理参数, 而在oc中没有一种标记变量是否有值得体系, 默认oc返回值和参数, 我们直接使用时候都会引发隐士解包, 如果oc返回的是nil值, 则在隐士强解包时候回造成程序崩溃.
  2. 实际上, 在swift中也可以定义隐士解包的值, 如var str: String!, 这种情况下, 如果直接使用str, 则会造成程序崩溃. 在实际开发中, 要避免这种写法.

虽然隐士可选值在行为上和非可选值一样, 但是我们依然可以对它们使用可选链, nil合并, if let和map, 所有的操作都和可选值一样.

16. swift线程锁

16.1 synchronized

func synchronized(lock: AnyObject, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}

16.2 dispatch_semaphore

使用dispatch_semaphore创建信号, 使用信号量来控制访问线程的数量.

17. 动态加载字体

/// 判断字体是否存在
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)
}
}

18. swift中的@convention

@convention是用来修饰闭包的, 用来表明此闭包可以兼容什么语言格式的闭包. 比如convention(c)表示此闭包可以传递到c函数中. 如下所示:

  1. @convention(swift) : 表明这个是一个swift的闭包;
  2. @convention(block) :表明这个是一个兼容oc的block的闭包;
  3. @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)")
}

19.swift获取对象内存地址

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
}

iOS/Swift Tips 1的更多相关文章

  1. iOS swift的xcworkspace多项目管理(架构思想)

    iOS  swift的xcworkspace多项目管理(架构思想) 技术说明: 今天在这里分享 swift下的 xcworkspace多项目管理(架构思想),能为我们在开发中带来哪些便捷?能为我们对整 ...

  2. iOS Swift 模块练习/swift基础学习

    SWIFT项目练习     SWIFT项目练习2 iOS Swift基础知识代码 推荐:Swift学习使用知识代码软件 0.swift中的宏定义(使用方法代替宏) 一.视图  +控件 1.UIImag ...

  3. ios swift 实现饼状图进度条,swift环形进度条

    ios swift 实现饼状图进度条 // // ProgressControl.swift // L02MyProgressControl // // Created by plter on 7/2 ...

  4. Building gRPC Client iOS Swift Note Taking App

    gRPC is an universal remote procedure call framework developed by Google that has been gaining inter ...

  5. iOS Swift WisdomScanKit图片浏览器功能SDK

    iOS Swift WisdomScanKit图片浏览器功能SDK使用 一:简介      WisdomScanKit 由 Swift4.2版编写,完全兼容OC项目调用. WisdomScanKit的 ...

  6. iOS Swift WisdomScanKit二维码扫码SDK,自定义全屏拍照SDK,系统相册图片浏览,编辑SDK

    iOS Swift WisdomScanKit 是一款强大的集二维码扫码,自定义全屏拍照,系统相册图片编辑多选和系统相册图片浏览功能于一身的 Framework SDK [1]前言:    今天给大家 ...

  7. iOS Swift WisdomHUD 提示界面框架

    iOS Swift WisdomHUD 提示界面框架  Framework Use profile(应用简介) 一:WisdomHUD简介 今天给大家介绍一款iOS的界面显示器:WisdomHUD,W ...

  8. iOS Swift WisdomKeyboardKing 键盘智能管家SDK

    iOS Swift WisdomKeyboardKing 键盘智能管家SDK [1]前言:    今天给大家推荐个好用的开源框架:WisdomKeyboardKing,方面iOS日常开发,优点和功能请 ...

  9. iOS swift项目IM实现,从长连接到数据流解析分析之Socket

    iOS  swift项目IM实现,从长连接到底层数据解析分析之Socket 一:项目简介:  去年开始接手了一个国企移动项目,项目的需求是实现IM即时通讯功能. * 一期版本功能包括了:       ...

随机推荐

  1. React Native之React速学教程(下)

    概述 本篇为<React Native之React速学教程>的最后一篇.本篇将带着大家一起认识ES6,学习在开发中常用的一些ES6的新特性,以及ES6与ES5的区别,解决大家在学习Reac ...

  2. 001Java锁之synchronized

    01.synchronized & Lock synchronized锁同步 软件层面依赖JVM Lock锁同步 硬件层面依赖cpu指令 02.synchronized作用域 方法:锁住对象实 ...

  3. RocketMQ读书笔记3——消费者

    [不同类型的消费者] DefaultMQPushConsumer 由系统控制读取操作,收到消息后自动调用传入的处理方法来处理. DefaultMQPullConsumer 读取操作中的大部分功能由使用 ...

  4. c++开发ocx入门实践二

    原文:http://blog.csdn.net/yhhyhhyhhyhh/article/details/51374355         IDE:vs2010,c++,测试工具,vs自带的TstCo ...

  5. Python爬虫教程-18-页面解析和数据提取

    本篇针对的数据是已经存在在页面上的数据,不包括动态生成的数据,今天是对HTML中提取对我们有用的数据,去除无用的数据 Python爬虫教程-18-页面解析和数据提取 结构化数据:先有的结构,再谈数据 ...

  6. IIFE

    一.IIFE IIFE:immediately-invoked function expression,即时调用函数表达式. 如果一个函数,在定义的时候,就想直接调用它,就是一个IIFE. 函数执行方 ...

  7. idea智能提示 不管用 问题

    今天碰到个问题,idea的智能提示 死活不能使用了.同一个包下的类竟然还没有智能提示,无语了... 搜了好多篇文章,虽然知道 重装可以解决...  这样设置就可以了:

  8. sqlserver中循环生成记录

    declare @i int set @i=1 while(@i<=10) begin INSERT INTO [BMData].[dbo].[QueryBlackListLogs] ([ID] ...

  9. SQL Server ->> 时间函数: EOMONTH, DATEFROMPARTS, TIMEFROMPARTS, DATETIMEFROMPARTS, DATETIMEOFFSETFROMPARTS

    上面几个函数都是SQL Server 2012新增的时间函数. EOMONTH 返回传入时间的月结束日,返回数据类型为DATE SELECT EOMONTH(GETDATE()) 结果为 DATEFR ...

  10. Spark 中的宽依赖和窄依赖

    Spark中RDD的高效与DAG图有着莫大的关系,在DAG调度中需要对计算过程划分stage,而划分依据就是RDD之间的依赖关系.针对不同的转换函数,RDD之间的依赖关系分类窄依赖(narrow de ...