- 获取未授权
RequestToken(登录界面)
- 按照文档发送
GET请求
- 按照文档发送
- 获取已经授权
RequestToken(让用户登录)
- 截取授权回调页code=后面字符串
- 换取
AccessToken(令牌)
- 传递已经授权
RequestToken
, 和其它参数
- 传递已经授权
- 守护者和可选绑定
-
guard let value = xxx else {}
- 有效避免
{}
嵌套, 提高阅读性 - 条件表达式中的变量可以在后面使用
- 条件表达式中的变量不能在
{}
中使用
- 有效避免
-
if let value = xxx {}
- 容易形成
{}
嵌套 - 条件表达式中的变量不能在后面使用
- 条件表达式中的变量可以在
{}
中使用
- 容易形成
- 守护者和可选绑定可以添加额外条件
-
Swift
// 两个条件同时为false进入{}
guard let value = xxx where 条件表达式
else
{
return true
}
// 两个条件同时为true进入{}
if let value = xxx where 条件表达式
{
}
```
-
NSURL
的query
属性- 用于获取
URL
中的参数(?
后面的内容)
- 用于获取
- 单例
-
dispatch
- 原理默认
dispatch_once_t
等于0
, 执行一次之后变为-1
*Swift
不推荐这样编写单例
- 原理默认
-
swift
static var onceToken: dispatch_once_t = 0;
static var instance: NetworkTools?;
class func shareInstance() -> NetworkTools
{
print(onceToken)
dispatch_once(&onceToken) { () -> Void in
instance = NetworkTools()
}
return instance!
}
```
+ 静态常量
* let是线程安全的
* let只能赋值一次
* 推荐写法
swift
static let shareInstance:NetworkTools = NetworkTools()
+ 如何在创建单例时初始化对象
* 静态常量 + 重写构造方法
swift
static let shareInstance:NetworkTools = NetworkTools()
override init() {
print("初始化操作")
}
* 静态常量 + 闭包
swift
static let shareInstance:NetworkTools = {
let t = NetworkTools()
print("初始化操作")
// 对T进行初始化
return t
}()
- AFN
+ Swift使用不同命名空间(不同项目)中的类需要导入头文件
```swift
import UIKit
import AFNetworking
class NetworkTools: AFHTTPSessionManager {
static let shareInstance: NetworkTools = {
// 注意: 指定baseURL一定要以/结尾
let url = NSURL(string: "https://api.weibo.com/")
let tools = NetworkTools(baseURL: url)
tools.responseSerializer.acceptableContentTypes = NSSet(objects: "application/json", "text/json", "text/javascript", "text/plain") as? Set<String>
return tools
}()
}
```
- KVC
+ key必须和属性一一对应, 否则需要重写
```swift
override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
```
+ 在构造方法中使用KVC必须先调用super.init()
+ 基本数据类型属性必须初始化, 否则会报错
- 打印对象
+ 重写CustomStringConvertible协议中的description属性
```swift
override var description: String{
let keys = ["access_token", "expires_in", "uid"]
let dict = dictionaryWithValuesForKeys(keys)
return "\(dict)"
}
```
+ 注意Swift2.0之前该属性在Printable协议中
- 归档解归档
+ 和OC一致
```swift
// 写入文件时调用
func encodeWithCoder(aCoder: NSCoder)
{
aCoder.encodeObject(access_token, forKey: "access_token")
aCoder.encodeObject(expires_in, forKey: "expires_in")
aCoder.encodeObject(uid, forKey: "uid")
}
// 从文件中读取出来时调用
required init?(coder aDecoder: NSCoder)
{
access_token = aDecoder.decodeObjectForKey("access_token") as? String
expires_in = aDecoder.decodeObjectForKey("expires_in") as! Int
uid = aDecoder.decodeObjectForKey("uid") as? String
}
```
+ 注意: 开发中谁最清楚怎么做, 方法就应该在谁里面
- 路径
- `Documents`
- 需要保存由"应用程序本身"产生的文件或者数据,例如:游戏进度、涂鸦软件的绘图
- 目录中的文件会被自动保存在 iCloud
- 注意:不要保存从网络上下载的文件,否则会无法上架!
- `Caches`
- 保存临时文件,"后续需要使用",例如:缓存图片,离线数据(地图数据)
- 系统不会清理 `cache` 目录中的文件
- 就要求程序开发时,"必须提供 `cache` 目录的清理解决方案"
- `Preference`s
- 用户偏好,使用 `NSUserDefault` 直接读写!
- 如果要想数据及时写入磁盘,还需要调用一个同步方法
- `tmp`
- 保存临时文件,"后续不需要使用"
- `tmp` 目录中的文件,系统会自动清理
- 重新启动手机,`tmp` 目录会被清空
- 系统磁盘空间不足时,系统也会自动清理
- 路径优化
+ 开发中经常需要往CachesDirectory/DocumentDirectory/TemporaryDirectory中存储数据
+ 为了方便拼接路径, 化繁为简应该对拼接实现进行封装
Swift
func docDir() -> String
{
// 1.拿到文件夹路径
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!
// 2.拼接字符串
// 注意: pathComponents方法, 会按照字符串的/来切割字符串, 如果给定的字符串中没有/那么就直接返回给定的字符串
let temp = (self as NSString).pathComponents.last
// 如果??前面有值, 那么??后面的代码不执行, 如果??前面没有值, 那么就执行??后面的代码
let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
// 3.返回结果
return result
}
func cachesDir() -> String
{
// 1.拿到文件夹路径
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!
let temp = (self as NSString).pathComponents.last
// 2.拼接字符串
let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
// 3.返回结果
return result
}
func tmpDir() -> String
{
// 1.拿到文件夹路径
let path = NSTemporaryDirectory()
let temp = (self as NSString).pathComponents.last
// 2.拼接字符串
let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
// 3.返回结果
return result
}
- `??`
+ `??`前面不为`nil`,后面不执行
+ `??`前面为`nil`, 执行后面代码
- 性能优化
+ 多次从文件中加载授权模型返回的结果一致
+ 从文件中加载模型非常消耗性能
+ 解决方案: 利用静态变量保存, 只加载一次
- 自定义UICollection布局
+ 在显示cell之前会调用prepareLayout准备布局
+ 在prepareLayout方法中修改布局
```swift
class NewfeatureLayout: UICollectionViewFlowLayout {
// 该方法会在显示cell之前调用
override func prepareLayout() {
// 修改layout属性
itemSize = UIScreen.mainScreen().bounds.size
scrollDirection = UICollectionViewScrollDirection.Horizontal
minimumInteritemSpacing = 0
minimumLineSpacing = 0
// 设置collectionView
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.showsVerticalScrollIndicator = false
collectionView?.bounces = false
collectionView?.pagingEnabled = true
}
}
- 监听cell是否完全显示
override func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath){
}
- 获取当前显示cell的索引
swift let path = collectionView.indexPathsForVisibleItems()
- Damping动画
/*
第一个参数: 动画时长
第二个参数: 延迟时长
第三个参数: 震动幅度 0.0~1.0之间, 值越小震动越厉害
第四个参数: 动画初始速度, 值越大开始就越快
第五个参数: 附加选项
第六个参数: 需要执行动画代码
第七个参数: 动画执行完毕之后的回调
*/
UIView.animateWithDuration(2.0, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
self.startButton.transform = CGAffineTransformIdentity
}, completion: { (_) -> Void in
XMGLog("动画执行完毕")
self.startButton.userInteractionEnabled = true
})
- 获取用户信息
- 保证方法单一性, 不要做过多复杂操作
- 将主动权交给调用者
func loadUserInfo(finished: (dict: [String: AnyObject]?, error: NSError?)->())
{
// 断言: 用于程序员之间沟通
// 断定access_token一定有值, 如果没有程序就会崩溃, 并且打印后面的message
assert(access_token != nil, "必须有access_token才能使用该方法")
assert(uid != nil, "必须有uid才能使用该方法")
// 1.准备路径
let path = "2/users/show.json"
// 2.准备参数
let parameters = ["access_token": access_token!, "uid": uid!]
// 3.发送Get请求
NetworkTools.shareInstance.GET(path, parameters: parameters, success: { (task, objc) -> Void in
// 3.1.对服务器返回的数据进行安全校验
guard let dict = objc as? [String: AnyObject] else
{
XMGLog("服务器没有返回数据")
return
}
// 3.2从获取到的用户信息中取出昵称和头像
self.avatar_large = dict["avatar_large"] as? String
self.screen_name = dict["screen_name"] as? String
// 3.3保存授权信息
// self.saveUserAccount()
finished(dict: dict, error: nil)
}) { (task, error) -> Void in
XMGLog(error)
finished(dict: nil, error: error)
}
}
+ 用到闭包, 不管三七二十一先写上 `()->() ` 再做其它修改
-
界面切换流程
-
检查新版本
- 思路
- 获取当前
- 获取以前
- 进行比较
- 更新以前
- 返回结果
- 思路
Swift
/// 判断是否有新版本
private func isNewVersion() -> Bool
{
// 1.获取软件当前的版本号
guard let currentVersion = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as? String else
{
return false
}
// 2.获取软件以前的版本号
let defaults = NSUserDefaults.standardUserDefaults()
let sandboxVersion = (defaults.objectForKey("xmg") as? String) ?? "0"
// 3.利用软件当前的版本号和以前的版本号进行比较
// 如果当前 > 以前 --> 新版本
// 1.0 0.0
if currentVersion.compare(sandboxVersion) == NSComparisonResult.OrderedDescending
{
NSLog("有新版本")
// 4.如果有新版本就用当前的软件版本号更新以前的软件版本号
defaults.setObject(currentVersion, forKey: "xmg")
// defaults.synchronize() // iOS7以后不用写
return true
}
NSLog("没有新版本")
// 没有新版本
return false
}
- 完善界面切换逻辑
- 注意统一管理, 便于后期维护
- 注意安全校验, 一般网络和通知都需要校验
// 发送通知, 切换到首页
NSNotificationCenter.defaultCenter().postNotificationName(SYPChangeRootViewControllerNotification, object: self, userInfo: ["message": true])
// 发送通知切换到欢迎界面
NSNotificationCenter.defaultCenter().postNotificationName(SYPChangeRootViewControllerNotification, object: self, userInfo: ["message": false])
// 监听通知切换界面
@objc private func switchRootViewController(notice: NSNotification)
{
// NSLog(notice)
// 1.取出userInfo中的message
guard let info = notice.userInfo else
{
return
}
guard let flag = info["message"] as? Bool else
{
return
}
// 2.根据message对应的值, 决定对应的界面
var sb = UIStoryboard(name: "Welcome", bundle: nil)
if flag
{
// 跳转到首页
sb = UIStoryboard(name: "Main", bundle: nil)
}
window?.rootViewController = sb.instantiateInitialViewController()!
}
网友评论