微博项目的试炼
当基本的框架搭起来后
注意使用对齐,
设置导航栏主题: 使用 attribute 属性字典来设置各种主题中的配置, 如: setTitleAttribute… dict:{NSFont…, NSShadow…NSForegraound…}
这样就不用在另外用 KVC 啊什么的去改变这些属性了, 用它本来就提供的接口
自定义搜索栏: 继承自 UITextField, 才能自己定义背景图片, 记得设置好内容, leftView 的 contentMode ,
设置 titleVIew, 将按钮设置上去, 增加其 title 的功能, 文字和图形的左右互倒, 直接填充式的布局
弹出菜单,
设置弹出菜单可以自己定义类, 由 cover(隔绝其他控件的操作), container(装载内容的容器), contentView(暴露出来, 给外面设置内容的属性, 即最终展示的 view)
可以增加一些功能
为了保证操作的层次不同, 不互相影响, 就不能在父控件来操作, 可以自己加个控件来操作(如 cover),
封装的东西应该有扩展性, 易用性, 不应该写死
研究 UITabBar 结构
从 tabBar 自身修改它的属性看看能不能改到我们想要的效果
因苹果闭源, 有些控件不开放接口, 我们不能直接使用. 可以通过继承的特性, 用父类指针指向他们, 然后看看他们的属性和类,
在 viewDidAppear方法里打印能获得的对象, 看看里面有什么, 还可以继续深入
通过从 NSClassFromString 来获取类名判断某个子控件是什么类, 然后符合的话再继续遍历打印看里面有什么,
分析子控件里的子控件的某些属性, 看能不能为我所用, 用了能不能达到效果
通过打印私有类的 superclass, 看看这个私有类继承自什么, 我们就可以强转这个类型来做一些操作
通过逐级遍历, 判断下是否我们需要的类, 是的话就进行相关操作, 在不确定能进行什么操作时, 可以打印看看继承自哪个类, 就可以利用父类的属性来进行操作
在 iOS7中, 选中的 tabBarButton 没有背景图片的属性, 不能判断是否被选中, 有个方法
通过获得当前选中的 item.title, 判断是否与 tabBarButton 中的 lable 一样, 则可知是否被选中, 然后修改选中按钮的字体颜色
自定义 UITabBar
默认情况下, tabViewController.tabBar是 readonly 的, 不能直接改, 但是, 我们可以通过运行时机制, 在运行的时将其 tabBar 改成我们自定义的 tabBar
#import <objc/runtime.h>
/**
* 在运行时将自定义 tabBar 替换自带的 tabBar
* 一般可以在运行时替换的话, 也可以通过 KVC 将自定义的 tabBar 设值到对应的 key 上, 即 key 为其属性名 :"tabBar", 因为 KVC 是直接赋值给_成员变量
*/
// objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
可以通过打印内存地址查看系统的 tabar 是否换成了自定义 tabBar
看看能不能在数组中通过获取某个类的对象出来 , 不行
遍历私有类型的子控件, 判断需要进行操作的类, 通过使用父类指针来获取子类成员, 通过父类的属性进行操作, 如果还是不兴的话, 再继续遍历, 知道想要的类出来, 强转为父类尽心操作
靠靠靠! 本来就有可以设置 item 的字体的属性, attribute.
可以设置字体, 颜色, 状态, 像之前设置主题一样, 虽然不是什么黑科技,,,但是一样的效果, 实现更简单易懂...这才是王道啊...
试试: 将 item 的设置 title 属性封装起来, 内部自己设置, 统一起来就好
监听加号点击:
设置 tabBar代理, 监听点击, 内部在加号点击后传给代理, 然后 tabBar 控制器 modal 一个 navC 包着的控制器, 内容在自己的模块设置,
图片拉伸细节:
图片的拉伸方式, 和以前想的一样, 按线来拉伸的话, 拉伸过大, 会造成一个截面横拉. 所以有箭头的地方, 不要拉伸太大, 尽量使用箭头所在线是长的, 这样就不会过多的拉伸箭头
版本新特性
当第一次使用一个新版本时, 会有个版本新特性界面, 根据对版本的判断, BundleVersion = ? . 来加载 rootViewController, 首次就显示版本新特性, 不然就是 tabBarViewController
从沙盒中获取上一次保存的版本号: [NSUserDefult...] valueForKey:];
获取当前运行软件的版本号: [NSBundle mainBundle] infoDictionary]; key:CFBundleVersion...
因版本号可能是1.1.1....所以不能转化为数字, 可以直接判断字符串是不是相同. 一般来说更新版本号都是往上的,
不相同就显示版本新特性, 然后在将当前版本好保存, 点击后显示 tabBarController
相同的就直接显示tabBarController
记得 makeKeyAndVisible!!
从 info.plist 中获取版本号对应的 key, 还可以 用 kCFbundleVersionKey, 可以获得 key, 但是是 CF 类型的, 转换成 NSString *类型需要桥接__brige_type
不同屏幕适配图片
在不同的屏幕间适配图片时, 加上去的图片有时候美工做的命名有568@2x, 意味着在4寸屏上使用, 但是加上去的时候下面会显示unassighn, 表示这个图片项目不可用,
这样在编译的时候就不会把这个图片放到沙盒中, 应用就不能找到这个图片, 自动加载@2x的只有启动图片
可以将设置颜色的代码封装成宏或者分类
pageControl设置跟随滑动来指示分页时, 过半分页可以使用四舍五入的方式, 在算出第几个点时+0.5, 就不用在开始时加上半个屏幕
/**
图片的加载:
[UIImage imageNamed:@”home”]; 加载png图片
一、非retina屏幕
1、3.5 inch(320 x 480)
home.png
二、retina屏幕
1、3.5 inch(640 x 960)-
2、4.0 inch(640 x 1136)
home-568h@2x.png(如果home是程序的启动图片,才支持自动加载)
三、举例(以下情况都是系统自动加载)
1、home是启动图片- iPhone 1\3G\3GS – 3.5 inch 非retina :home.png
- iPhone 4\4S – 3.5 inch retina :home@2x.png
iPhone 5\5S\5C – 4.0 inch retina :home-568h@2x.png
2、home不是启动图片
- iPhone 1\3G\3GS – 3.5 inch 非retina :home.png
- iPhone 4\4S – 3.5 inch retina :home@2x.png
iPhone 5\5S\5C – 4.0 inch retina :home@2x.png
3、总结
- home.png :3.5 inch 非retina
- home@2x.png :retina
- home-568h@2x.png :4.0 inch retina + 启动图片
*/
/**
创建了一个控件,就是看不见
1.当前控件没有添加到父控件中
2.当前控件的hidden = YES
3.当前控件的alpha \<= 0.01
4.没有设置尺寸(frame.size、bounds.size)
5.位置不对(当前控件显示到窗口以外的区域)
6.背景色是clearColor
7.当前控件被其他可见的控件挡住了
8.当前控件是个显示图片的控件(没有设置图片\图片不存在,比如UIImageView)
9.当前控件是个显示文字的控件(没有设置文字\文字颜色跟后面的背景色一样,比如UILabel、UIButton)
10.检查父控件的前9种情况
一个控件能看见,但是点击后没有任何反应:
1.当前控件的userInteractionEnabled = NO
2.当前控件的enabled = NO
3.当前控件不在父控件的边框范围内
4.当前控件被一个背景色是clearColor的控件挡住了
5.检查父控件的前4种情况
6.。。。。。。
文本输入框没有在主窗口上:文本输入框的文字无法输入
*/
warning Note: 注意!!!注意!!!!, 在设置 center 之前!!需要先设置好 size!! 不然 center 没有参照, 会设定为原点!!!
不同个分辨率和屏幕大小会加载不同的图片, 根据@2x 来区分, @2x 肯定是加载 retana 屏的, 其他的3.5寸都是加载一般图片, 4寸会加载-568h@2x 的
控件中的三种 inset
contentInset: 整个内容的 inset
imageInset: 图片区域的 inset
titleInset: 文字部分的 inset
切换到主界面:
显示新特性在显示后不用返回的, 也没有设置有按钮让他返回, 所以它会一直:
modal: 一直是 window 的根控制器, 不释放, 浪费内存
push: 一直在栈底, 也不可释放,
应该直接替换 window.rootViewController
OAuth 授权
应用要访问某个用户的数据, 就需要用户对应用进行资源授权, 也就是要用户登录, 而为了数据安全, 应用不应该知道用户输入了什么,
这时登录界面应该由新浪提供, 一个登录网页, 我们就等着接收验证结果, 用户的过程我们不知道, 这个授权就是 OAuth 授权
* 获取未授权的 request token: 由新浪发来授权登录页面
* 登录成功后, 获取授权的 request token
* 用登录成功的 request token 来获取 Access token: 一般登录成功后会跳转到回调页面, 后面有 code, 通过这个来获取Access token
要想获得用户来授权访问用户的数据, 得先要成为新浪的开发阵营的一员,
* 在 open.weibo.com 中加入开发者-> 创建应用, 填写信息, 填写应用名, 回调页面,
* 查看开发文档的接口, 看获取授权界面的接口, 需要传递的参数, 然后发送请求过去
* 获得 response 未授权Request token -> 输入帐号密码登录后会出现授权界面(未授权过的只会出现一次, 当授权后应用获得 Accesstoken 后就不出现了) -> 回调页面
* 上一步都有 web 端完成, 应用不可知(另外可以一直监听键盘输入, 或者无限截图来获取看输入的密码这种无良的事情),完成后会回调页面, 应用的 webView 代理调用 shouldLoad..
* 调用代理的 ShouldliadURL...方法时, 判断有没有回调页面的 code 字符串的 rang 来获得 code
* 提取出 code, 利用 code, 查看 API中获取 Accesstoken 的接口, 传入参数, 发送请求
* 新浪服务器返回一个用户数据的字典. 里面就包含了 AccessToken, 应该将 AccessToken 保存起来, 之后的访问数据什么的都用到, 直接保存用户字典, 在默认的 AFN 请求中接收的 response 不处理 text/plain 类型的内容, 所以还要做处理,
控制器逻辑的搭建
* 进到应用首先判断有没有授权, 有了再进到是否新版本的判断, 没有就去加载授权页面获取授权,
* 用户字典可以用一个模型封装起来, 还要加上过期时间, 返回用户字典时判断过期没, 过期就返回nil,
* 可以将加载哪个根控制器的功能封装到一个工具类来, 需要判断加载的时候就调用这个工具类就好了
* 保存和获取用户字典可以交给一个工具类来处理, 统一管理用户字典的获取, 外界不用关心细节,
*
* 查看获取home的文档接口, 发送请求, 获取数据(在tableview里, viewDidLoad), 获得数据候记得刷新tableView
内容获取:
* 查看API文档获取数据的时候, 发送的参数, URL, 返回的数据类型, 结构, 字段, 字典对应的解释等
* 获取数据候根据数据的格式进行解析, 将字典中相关的数据放到相关的view的控件中展示, 应该将数据封装成模型,
* 将获得的微博数据字典转成模型
* 如果属性太多, 模型又很多的话, 一层层转换模型会很....所以用第三方框架来转模型. MJExtension 一行就可以转换多层模型, 简单设置就可以, 很方便
转换的模型里有数组, 数组里需要A 模型时, 需要在拥有这个数组属性的类的实现里重写上- (NSDictionary *)objectClassInArray 中设定字典, key = 属性名, Value = 类 class
还可以从文件中的字典加载进来了自动转化为模型
模型嵌套模型也可以不用管了, 除了模型中的数组也需要模型的需要重写方法声明下模型
检测运行时的代码是 C
OC 的底层是 C, 靠编译器 clang 将 OC 转成 C
将 main.m 坐在路径在终端打开 -> clang -rewrite-objc main.m -> 生成一个 cpp 文件, 就是运行时的 c 代码
-> 将 main()中的强转类型小括号都去掉, 可以发现都是 C语言函数, 像某个"对象"发送"消息"
可以在官方文档中搜索 runtime -> The objc_msgSend Function
运行时强大到可以动态增删改方法, 因为它是在运行时操作底层, 因为程序跑起来是在内存中, 修改内存肯定是用 C
现在微博有 SDK, 权限更多, 之后好好了解下
监控网络状况
为了更好的应用交互, 在不能联网的时候要做提醒, 不能让用户认为是软件的问题, 使用 AFN 来监听网络状态
AFNetworkReacherbilitymanager 可以将监控网络状态的代码抽取出来....反正也不依赖什么东西...
集成下拉刷新
iOS 的 UIKit 中已经自带有一个刷新控件: UIRefreshControl, 里面有使用说明
刷新的时候调用微博 API 获取最新的数据, 如果直接调用homeLine 方法的话, 会请求最新的 n 条数据直接覆盖原来的模型, 就会有些数据重复加载, 理想状态应该是加载我没有的新的数据, 并把数据放在最上面
每个微博都有个 id, id 自增, 所以最新的微博 id 最大, 且在最上面, 我们应该调用 since_id 这个接口, 获取我们最大 id 后面的微博
发送请求获取新的 id, (当本地模型有值时才赋值 since_id), 将最新的模型数组 insert 到原来数组的最前面, NSIndexSet *, 确定范围
刷新 tableView, 刷新后调用 refreshCOtrol 的 endRefresh 回复到默认状态
提示最新微博数
当加载了最新的微博数据后, 获得新模型数组,可以看看里面有多少条, 将新微博数量传递给一个展示新数量的方法, 可以设置一个属性 Label, 当需要展示的时候就 transform 出来, 然后在收回去, 也可以需要时候 创建, 显示完后销毁
过程大概是这样... 进入微博, 来到 home -> 自动调用刷新微博 -> 获取新微博拼接 -> 展示新微博数量 -> 刷新tableVIew -> 回复控件默认同时收起提示
效果是在导航栏后面出来, 所以不能是 tableView 的子控件(会随之滚动), 也不能是 window(会覆盖导航栏), 应该是导航控制器.View, 这样就可以管理导航栏和提示 Label 的层级关系
集成上啦刷新
想看更早的微博的话,在滚到底部的时候, 上啦加载更早的微博, 首先要有个上啦刷新的控件,
做一个有 Label 和菊花的 view, 添加到 footerView, 这样每次滚到底部就可以看到
问题: 在刚加载 view 的时候就能看到了, 应该设置隐藏, 监听 tableView每次刷新数据的过程, 判断有没有数据, status.count == 0. -> hiden
恩...妹的,,,视频少了一集2集, 添加控件后怎么让它上啦刷新和滚动到底部刷新仍然是迷....幸好哟代码..自己看看代码来实现一下,...总结总结...
分析代码: 通过监听 scrollView 的滚动, 获取 OFfset 的位置, 当大于某个值时就判断松手刷新, 显示 footerView 正好是 contentSize-屏幕高度+tabBar 高度
loadMoreData 好像有点问题………使用了不可变数组的方法, 返回了不可变数组实例, 实际上已经不是可变数组了, 报错找不到方法
自定义 UITextView
有时候自带的控件有功能上的不足, 我们就可以自定义控件,
如果还想拥有地带控件的某些属性, 仅仅是想补充一些功能的话, 应该用继承
UITextField: 有 placeHolder, 不可换行, 不可滚动
UITextView: 没有 placeholder, 可换行, 可滚动
我们自定义 UITextView
可以试听一个 Label 来展示 placeholder, 这样我们操作也方便, 面向整个 view 对象
首先要考虑严谨, 当外界改动的地方时, 什么地方需要重新适配
控件创建时就初始化label, 的一些属性, 在 layoutSubviews 设定位置
修改 placeholder 时要重新算 frame
设置 self.font 的地方也要同时设置 label 的 font
监听事件三种方式
代理: 只能有一个代理, 通知外部, 不建议自己做自己代理, 只有一个同代理
addTarget: UIControl 才有的方法,
通知: 一对多, 可以监听通知然后做点什么,
在文本改变时也要做出相应的隐藏 Label 的反应
自定义 toolBar
布局按钮, toolbar 背景色, 按钮的布局平局分配, 监听点击将
> 打开系统相册方法: UIImagePickController
通过将toolBar 上的按钮点击事件传递给代理, 让代理控制器知道点击的按钮, 然后做些控制器级别的操作
控制器中对传入的按钮的判断不应该用其image 来判断, 因为这是封装在 toolBar 里的, 可以通过 enum 来传到代理,
将 toolBar 放到发微博控制器上, 不作为键盘的辅助视图, 可以一直保留. 通过监听键盘弹出通知来随键盘一起移动
textView 完善
* 拖拽
* toolBar 本来就在底部
* 将弹出键盘成为第一响应者放在 viewWillAppear 里,快点弹出来...
选中图片
@@ 将九宫格的代码抽取出来....
通过 UIImagePickerCpntroller 来打开照片选择器或者是相机, 选中图片或者拍照完成确认使用后会回调代理方法, 传回一个信息字典, 图片就包含在字典里info[UIImagePickerControllerOriginalImage];
因为自定义的 textView 在其他很多地方可以用, 所以没必要放一个展示图片的 View 上去, 图片 VIew 另外定义, 有发微博控制器自己添加使用
记得在控制器中 dealloc 里移除对键盘时间通知的监听
已完成因目前模拟器打不开相册, 展示自己放入图片实验, 之后更新后在试一下/// 是模米奇版本或者是xcode版本的问题
发送微博,
发送微博的接口对于开放的接口来说分为2种, 一种发纯文字的, 一种带图的, 必需分开使用,
传入要求的参数, 将上传的图片转为 data 拼接上去
发送就好了.....成功的话会返回发送微博的详细数据
首页完善
界面完善
首页标题: 用户昵称
调用 API 获取用户信息, AccessToken, uid, 将按钮的标题设为呢喃, 大小根据传入的文字来动态改变(好像我已经做了...)
在获取呢喃后将呢喃存到账户的属性中, 每次加载首页的时候取出保存的名字显示, 一开始没有的时候默认显示首页, 属性中保存了 name 的话就显示 name,
(我觉得吧, 在应用加载能访问用户数据的时候就顺便获取了信息保存下来, 加载首页的时候就直接获取账户中保存的 name 来使用, )
bug: 登录时弹窗不退出, 在加载页面的时候有加载两次….准备:运行程序, 跟踪断点
解决: 在认证成功时hide
原因: 点击登录后还会加载某个网页, 加载授权页面的默认页
bug: 获取用户名设置后会显示了名字, 但是按钮的大小没有及时改变, 要重新打开才行
暂时解决: 在获取名字的时候不直接设置 titleView, 而是重新调用 configNav 方法
封装网络请求:
当前写的代码中有很多地方用到了 AFN, 这样存在风险: 当 AFN 停止维护之后, 依赖的项目就没有更多的保障了,
所以应该将业务的逻辑封装起来, 里面实现的技术细节封装, 项目中不需要知道实现的技术细节, 以后更换实现的技术就可以, 而且也更方便维护
技术的更新迭代迅速, 不应该让应用项目强依赖某个技术, 应该设置一个管理类来实现具体使用的技术细节,这样收外界影响的因素就只有自己的类
通过传递 block 形参, 在方法调用完成网络请求后调用这个 block, 并传递一些参数. 这些参数要先判断有没有值, 有值才调用, 不然报错
不要直接将自己方法中传入的 block 传给具体实现框架, 自己用的 block 可能参数的数量类型不一样, 应该在实现技术的调用 block 中调用自己传入的 block
关于发送图片的封装: 参数里面有 formdata 的东西, 还需要研究一下
封装微博业务
* 请求参数面向模型: 请求时传入的 URL, 参数等对控制器来说了解过于清楚, 参数又是字典, 不利于维护
* 请求结果面向模型: 返回的结果也是一个转换 json 的字典, 应该也转换为模型, 在返回给控制器
* 对控制器来说. 应该屏蔽业务细节
* 总的来说, 将微博额定业务逻辑封装起来, 控制器只管做事, 不管其中的细节, 这样在微博的网络逻辑中要改什么具体也只需要在一个地方改, 其他地方不会改动, 屏蔽业务细节, 控制器只需要知道要去做什么, 不用关心怎么做
封装一个业务处理类来专门处理微博的业务细节
在模型转字典时用 MJ 的框架的话, 对模型的设计不能是多个参数通用, 不然在转成字典时会将不需要发参数发送给 API, 可能会报错
将请求参数中的基本数据类型转成对象 NSNumber, 这样当没有设置的时候默认是 nil, 不能成为字典的一部分, 就不会将这个键值对传出
这样可以解决参数通用, 不需要的参数就不设置, 转成字典的时候不会转换 nil 的键值对, 只有设置值的键值对,
分层封装的原则: 上层只能依赖于下层, 不能跨层依赖, 而且不能反向依赖, 下层不应该依赖上层
之前将获取用户信息封装到了YCAccountTool 里, 这个应该属于用户工具类UserTool, 不应该是授权账户工具
封装业务逻辑:
* 新建一个模型类封装请求参数
* 新建一个模型类封装请求结果(返回结果)
* 新建一个业务类封装专一的结果
不用想着一次就把所有相似的东西都放到一个类里, 为了扩展性和适应性, 可以分类, 之后在将相似的抽取到父类即可, 更加面向对象, 不同的对象,业务逻辑清楚
抽取获取 AccessToken 的业务逻辑: 因为也是想服务器发送请求, 直接可以在业务方法中继承 baseTool, 传入模型, 设定返回类型, 调用就完了, 连 API 的地址都不需要知道, 这个业务逻辑知道就可以了, httpTool 也不需要知道, 因为url 是不定的
bug: 请求失败: Error Domain=com.alamofire.error.serialization.response Code=-1011 “Request failed: method not allowed (405)” — 请求方式错误, 应该是 POsT
bug: 请求成功后获取用户信息失败: 原因: 读取保存的用户信息时获取到 expire_end = nil; —在归档时只设置了通过字典保存时才计算 end 的值
解决: 重写 in 的 set 方法, 在设置 in 的时候就计算并设置好 end
优化: 可以在父类 tool 方法中直接传入外面传入的 successblock
显示消息未读数
微博提供了接口来获取当前各种未读的信息数, 这些由服务器维护, 我们调用就可以
设置新的参数模型个结果模型, 传入方法(在用户工具类中新建一个方法, 来获取未读数), 获得未读数模型后将对应的数字设定到tabBar 标签上 bageValue, 设定定时器定时刷新获取未读数.
在程序进入后台后会处于挂起状态, 定时器停止, 就不能调用方法获取未读数, 我们应该在它后台时能继续运行来获取未读数, 但是移动设备内存有限, 不能随便后台, 我们需要向系统申请, 来开启后台
UIBackgroundTaskIdentifier taskId = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:taskId];
}];
系统会自动判断应用可以留存后台多久, 这个我们不能确定,系统会将优先级高的留存久一点
我们可以做一些配置欺骗系统, 来燃应用留存久一点: 配置info.plist 文件, request Background...声明本应用是音视频应用, 需要后台, 而且在代码里循环播放一个0k 的mp3文件, 调用播放接口. 就可以留存久一点
停止后台会调用一个 block, 我们要在调用时立即结束这个后台
完善细节
NSTimer 在加入到 NSRunLoop 时, 默认是 default 模式, 这个在主线程执行 ui 操作时会停止其他事件, 应该设置为 commend 模式, 这样在主线程执行 UI 事件时也会尽量抽时间去执行 Timer
timer 刷新的间隔时间应该设置长一点, 不然会耗电量大,
间隔时间长的话, 在刷新微博时, 还有未读显示在下面----在刷新显示新微博数的时候清空 tab 数量, 顺便减去应用图标显示的数量
最好还能当点击 tabBar 按钮的时候回到顶部, 如果有未读消息的话就刷新...这个用系统自带的 tabBar 的话有很多的不足
连接抓包软件, 监听手机的网络请求, 看看什么软件发送了什么请求到哪里, 返回什么数据, 我们可以截取下来自己做一个东西,
* 电脑装 charles, 开启代理,
* 将手机连上的 wifi 中设置手动代理, 地址设为电脑 ip, 端口随意
* 在手机上访问点什么, Charles 就能拦截到这些请求和响应
完善主页 cell
* cell 中有多种显示模式, 但是其实就是一种, 可以先把可能要显示的所有控件都加到 cell 中, 再根据某些属性决定要不要隐藏这个子控件
自定义 cell 的步奏
1. 新建一个继承自 UITableViewCell 的子类
2. 在initWithStyle 方法中进行子控件的初始化
* 将所有可能显示的子控件都添加到 contentView 中
* 顺便设置子控件的一些属性(一次性的属性, 文字, 颜色, 背景等)
3. 提供2个模型
* 数据模型(文字+图片数据)
* frame 模型(里面包含了数据模型, 根据数据模型算出对应的 frame, 对应的模型只要算一次就好, 不然 cell 重复加载会重复计算耗能)
4. cell 提供一个属性传入 frame 模型
* 将 frame 模型传递给 cell
* cell 根据 frame 模型给子控件设置 frame, 根据数据模型设置 cell 的数据
* cell 根据数据模型决定显示和隐藏那些子控件
5. 在 tableView 的方法但会 cell 的高度
对 cell 中的控件进行分层, 可以便于维护, 清晰业务逻辑
原创内容层
头像, name, 来源, 时间, 微博内容
转发内容层
转发用户 nameLabel(button), 内容
toobar
三个按钮
cell 的 frame 分层
根据内容的分层来对 frame 模型分层
cell.frame 模型
原创内容 View.frame
转发内容View.frame
toolbar.rect(按钮不用根据什么来该拜年 frame, 可以固定提供)
ContentView.rect(包含原创和转发的内容)
frame 的模型和 cell 的 view 一一对应, 这样方便管理, 逻辑清晰
每个层只管管理, 到最后一个拥有具体子控件的层时再做计算
完善细节
在 UIView 当中, 需要用到背景色是一张图片拉伸的话, 可以在控件的 drawRect 方法中将图片drawInRect 的方法绘制上去
或者这个 retweetView 继承 ImageView, 设置 image...
在 cell 之间空出空隙能看到背景色, 可以设置 cell 的高度高一点, cell 的背景色 clear, 在将整个 detailView 设置背景色
可以设置里面分层的 view 是 imageView, 这样可以设置高亮和一般的颜色
添加会员显示图标, 根据会员的等级来确定图片
------------------------------------------------------2016.6.19
时间处理
在数据模型中重写 get 方法, 这样可以保证通过模型来操作的其他事情都是以我们返回的为准, 在get 中进行转换, 判断
使用时间格式类将字符串转成某个格式的 NSDate, 转换钱要先设置格式者的时间格式, 转换成 NSDate 后, 再设置时间格式者的格式转成我们通常的格式
或者, 在不转换的时候, 用 NSCalender 类, 根据 NSDate 来取出 calender, 可以取出其中的 compennet, 里面包括了日期的各种信息
我们就可以比较年/月/日/时间
下一步对这时间进行分层处理, 排除>1天的, 排除>1h 的, 排除>1m 的...
在每次 get 数据的时候, 都判断当前时间和数据的时间的 delta, 来返回需要是刚刚/几分钟钱/几天前/ 那一年
用 calender 从 date 获取出需要的组件 Component, 根据需要进行比较, 还能获得两个 date 的差值
在设置 frame 时, 每次赋值 cell.frame 都会拿到时间的 frame 设置到子控件上, 所以在时间.frame 的 get 方法中再次重新计算当前时间的 frame
来源处理
新浪传回的数据是一个超链接字符串, 我们要截取其中的客户端文字,
在转换模型的时候就进行提取保存, 这个数据不变, 我们只需要提取一次
可以通过获得">的位置获取文字的起始位置 NSRangeof...
再获得</ 的位置location, 减去之前获得的位置, 就是需要的位置的 range
取出这个 range 拼接其他文字来返回这个来源属性
但是这里有个注意点, 这个不同与时间, 需要每次 get 的时候都判断, 来源都是不变的, 所以在 set方法将目的字符串抽取出来做一次保存就行
在没词 cell 拿到 frame 时, 因为来源是依靠时间 frame 的, 时间改了, 来源也应该跟着缩进, 在来源 frame 的 get 方法中重新根据时间的 frame 来计算自己的 x就行, 其他的不变
toolBar
在 toolBarView 的创建中直接就跟着创建子控件---三个按钮, 两个分割线
toolbar 也应该继承自 UIImageView, 它也有背景图片
记得调整按钮高亮不调整图片
将创建按钮的方法封装起来, 还有创建分割线的
让 toolbar 交互
按钮的 title 缩进
分割线的中心处在按钮的分割点
在按钮的显示上优化, 有数字显示数字, 没有就显示默认的, 当数字范围是多少时应该友好显示
可以尝试
在用 calender 的时候, 有个不用转成年月日来比较, 直接用 date 比...反正都是 date....
改下返回的数据, 模拟测试环境, 测试返回数据的不同有什么反应,是否有 bug
显示配图区间
* 原创或者转发中都应该有配图属性, 位置就在正文下方, 自己调整, 这个整个 View 里在放哥各个图片, 统一管理
* 宽高的设置要各部图片的个数来确定, 可以先定死图片的宽高, 最大列数, 总列数即为超过最大列数就是最大否则按图片数
* 知道最多显示多少列后, 可以用公式来知道一共显示多少行:(总个数+每行几个-1)/每行几个 = 多少行
* 图片区域在转发中也有, 应该把计算尺寸Size抽取出来, 可以自己定义一个专门对图片的 frame, 或者封装在图片 View 中
* 将模型中的 urls 传给大的 view, 再取出每个 url 中保存的 pic 模型传给小的 imageView,
* 由 imageView 拿出送略图显示图片, 并且判断是否 gif,决定是否展示 gif 标志图片
* 判断后缀前缀: hasPrefix....转小写: lowerString
* 拿出路径扩展: String.pathExtension ,
放大图片
* 让 imageView 响应点击, 要先让它使能交互, 还要添加手指识别器, 而且它的父控件都需要支持交互
* 可以通过设定 tag 来识别是哪个 imageView 被点击, 识别器中拥有锁添加到的那个 view.
* 一个手势识别器只能添加到一个 view 上
* 在 photosVIew 中添加手势, 因为在监听点击中获取所有本 cell 的配图, 用 Photo 自己 监听的话太局部了
* 思路: 在点击时立刻有蒙版上去, 且图片显示在最上层.
* 可以在点击时创建一个蒙版, 然后创建一个与点击 view 一模一样的 view 为其子控件, 在显示后让这个子控件放大至填充
* 子控件 imageView 的坐标和原来的不同,不再是以 photosView 为父控件,而是整个屏幕的蒙版,所以可以用iOS 的方法转换坐标系
* 转换坐标系,就是转换参照的原点, 将原来父控件的参照点改为某个, 就可得出转换坐标后的 frame,
* 元父控件 convertRect:要转换的frame TOView:目的父控件
* 点击的时候显示出来否放大: 就将整个宽度设为整个屏幕的宽度,教程是直接设置 frame,我用 transform试试,
* 拿到显示在 cover 上的那个图片, 并且保存转换坐标后的 frame, 在点击回去的时候, 将图片设回原来的位置, cover 慢慢透明,最后完成时移除, 设定中间 frame 时用 x,y 设定也是可以的
* 其实,显示多张图片还有很多都东西要完善, 比如显示下载进度, 展示的图片不应该是缩略图, 应该将Photo 模型加一个原图属性, 点击后加载原图, 缩略图做占位图, 有多张时候可以滚动, 滚动时候同时下载要显示的那张, 还有图片数, 滚动后回去还是对应的位置,
* 涉及到图片浏览器, 合格的应该节约内存, MJ 封装有一个处理很多细节的图片流量其类, 拿来用就能实现基本功能, 可以参考写一个
添加表情键盘
当点击表情按钮的时候, 切换成自定义键盘, 可以先用一个 View 来测试, 需要使用输入框的 inputView, 才能让他自动相应输入框的点击时间, 切换输入框,就可以切换到自定义的 view, 在改变 inputView 的时候, 是没有立即反应的,要重新成为第一响应者的时候才会看到效果, 先登出,在注入第一响应者.
点击时改变按钮的图标, 可以在监听点击事件时, 判断是表情按钮, 就将表情按钮的图片改变. 或者提供一个方法,显示 emotionImage,内部将图片更换,
当点击切换的时候, 不应该让工具条下去, 而是保持: 可以设置一个是否切换的 BOOL, 监听 hideKeyboard 的时候判断这个 BOOL. 是切换状态的话就不做反应
添加表情键盘的时候, 分为两部分, 上半部是表情列表, 下面是工具条(可以直接写在 emotionView 里, 用 view 封装在一个就行, 不用在弄个类,)
设置工具栏按钮的时候, 抽取出方法来, 因为每个按钮的文字都不同, 在添加方法里判断当前添加的是第几个: 看toolBar 里的按钮有几个,添加进去后肯定是最后一个, 所以个数即是它的第几个.新浪根据按钮的位置有不一样的图片, 所以这里要判断,
表情键盘的建立 TT2016.06.25 ~~
给自定义键盘传入 emotion 模型后, 计算出有多少页, 根据模型设定指示器数量, 创建 scrollView 的子 View,数量岁模型,
注意这里是在没词传入 emotions 的时候, 都会为 scrollView 创建子 VIew 添加到上面, 这样会累加, 应该每次添加前先清掉之前的 .
再首次创建 scrollView 的时候才会调用 layoutSubViews,所以在点击切换表情的时候,子 view 是没有 frame 的, 没词创建好后要带哦用 setNeedLayout
每个子 view 都有很多内容和操作, 且都应该自己管理好里面的控件, 应该单独封装起来,
注意, 在最后一页可能会有不够每页的数量,直接取 range 的话会发生数组越界...需要对越界进行判断处理,
每个表情的按钮用 button, 因为 emoji 表情是字符,在系统里转码显示的.每个button应该仅仅挨着,图片直接设置 Image 居中,这样在滑动选择的时候就能一过就是另一个
在用 NSBundle 来获取内容时资源可以是带路径的资源(用与在 bunble 里存在的文件夹里的文件)...图片的话直接 Imagename 传入带文件路径的 string+name.png`也可以拿到 bundle 中文件夹中的资源....
运用数组的 makeObjectPerform...可以让所有数组元素调用某一个方法.设定好文件夹属性后, 就能从 BUndle 里获取资源了,
在点击不同的标签时, 应该让 page 回到第一页:在每次点击时, 设定为第0页, 滚到最开始,
显示 emoji: emoji 在内存中是以十六进制存储, 要把它转为 unicode 字符,可以用一个别人写的分来来转换
emoji 的大小取决于字体大小, 值相当于图片的宽高
生成随机数: arc4Random()
性能优化
* 在表情键盘中每次都要销毁和创建 scrollView 的子控件 gridView 很耗性能, 我们应该对差不多的东西重复利用,
* 在 gridView 中, 每个其子控件 button 也没必要重复创建, 或者,当 gridView 重用后,里面的 button 还在, 不做覆盖处理的话没词传入 emotion 模型又会创建 button 添加到上面, 最后的结果就是刚添加的 Button 被排到很后面, 而滚不过去, 我们也应该对 Button 进行覆盖重用
* 可以用这个思路, 需要的 view 和拥有的 view 哪个多就遍历哪个数量, 当遍历到多过拥有的时->创建, 不然就取出重用(当超过需要的时->隐藏并 continue), 最后拿到的 view 都要取消隐藏
* 每个 button 对模型还做了一些处理, 之后可能还会做跟多处理, 应该把其单独抽出来作为一个类
封装 emotionView
将 Button 封装起来, 传入模型, 在里面判断就可以了
挖掘子控件,
有时候为了调试, 我们可以将整个 window 的 subVIew 打印出来, 看下层级关系, 在遍历的时候将子控件的关键信息拼接成字符串给 xml, 在浏览器中打开就可以清楚的看到层级关系
单击表情按钮弹出提示 View
监听按钮点击, 用 gridView 监听, 因为还要用到一个 popView 的东西, 没必要重用,
将点击按钮整个传入, 就能知道按钮的内容和位置
创建一个 popView 将点击的按钮显示出来, 里面要有一个 emotionCell 来展示点击的按钮
在设置按钮的时候 去掉动画, 设置完后拿回动画
长按表情
添加手势监听长按
判断当前点是否在 emotionCell 的范围, 拿到判断返回的 emotonCell
没抬起手时, 结果传给popView, nil 就不做操作, 不然显示出来,
当收抬起时, 发送通知, nil 的话不做操作,
点击删除按钮也发送通知
在发微博控制器监听通知进行操作
最近表情
当点击表情能发送通知时才是确实发了表情, 这里应该放到最近表情中保存一份
可以使用单例类, 使数据在内存中只有一份, 这个类可以做一个管理者
也可以使用工具类, 创建几个 static 全局属性, 也可以保证全局中只有一份. 这种做法好像比较方便
最近表情可以从沙盒中获取这个最近表情数组, 通过 NSCoding 将 emotion 模型保存起来, 之后拿来调用放到 emotionCell 中
记得保存自定义对象时还要用 NSKeyedArchive 类
???? 解码过程中碰到有的关键字是没有对应到的
bug: 在重新打开应用后表情键盘添加到最近不能判断出原来已经有了,
---- 添加的时候判断手否存在时是靠传入对象的-isEqual 方法, 逐个传入自己数组中的每个元素进行内存地址判断,一样的就返回 yes, 但是重新打开后是不一样的地址.
重写-(BOOL)isequal:方法, 其中判断改为判断 png 或者 code
bug: 在没有键盘时点击表情按钮键盘出来, 再拖拽文本框键盘下去,但是 toolBar 没有跟着下去
--- 在按钮点击那一刻 isChanging = yes -> 退出第一相应者 -> 因为还没有键盘不会发 hide 通知 -> changing=No 不能调用, -> 代码过不到收回 toolbar 那里,
---- 应该把 changing = NO 卸载退出第一响应者后, 不管能不能收起键盘, 操作完成后都回复 changing 状态
小问题, 实时刷新的话, popView 还在显示本来 emotionView 时那个就跳到前面去了, 应该等 popView 消失时在刷新
1
图文混排
图文混排的几种方案
* coreText, 非常蛋疼, 纯 C
* NSAttributeString, 属性文本, 付文本, 里面提供了很多设置文字各种属性的接口, 使用简单
* TextKit, 使用简单, 封装好, 好用
NSAttributeString: 属性文本
* 对文字操作, 只需要将对应的属性设置给对应的文字(一般通过 range)
* 创建可变属性文本, 之后就可以对这文本设置一些属性
* 看接口有一些常用的属性: 颜色, 大小, 背景色, 下划线,
通过附件简单添加图片:
* 通过 attachment 属性给属性文本添加附件, 附件可以显示出来, 和文本排在一起
* attach.Image 可以设置图片.
* 通过 attach.bounds 可以设置附件大小, 要和文字一样就设成文字大小即可
UIWebView 显示图文混排
利用 HTML 的标签来给 HTML 文本设置属性
* HTML 本来就是标签语言, 通过标签元素来给其中的内容设置属性,
* 但是 webView 有缺点, 会有内存泄露, 但是通过这个排版代码量少, 不过需要知道 html 语言, CSS, 等网页相关的知识,设计连接简单
富文本的简单应用
* 发微博控制器的标题, 换行, 大小不一样,
直接用不好用, 可以设置 titleView = Label, 在用 Label 设置富文本
* 将表情点击后添加到文本框,
当表情点击后通过通知传递给控制器, 拿到 emotion 模型创建好 image, 创建 attach -> attributeString -> 添加到文本框拼接
最好在从 textView 抽取一个子类, 将传入 emotion 后拼接上去的方法封装起来.
文本和富文本的 font 控制方式不同, 要统一起来
对文字输入的判断, 代码直接设置的不会有通知调用, 需要在设置表情时自己调用, 并检测富文本的长度, 它包括的范围广, TextView 中的都包括
在拼接表情时, 还需要判断是 emoji 还是图片, 处理方式不同,
取出当前富文本, 拼接表情, 设置回 TextView 的富文本
* 表情中的删除功能
textView 自带有 deleteBackspace, 调用删除功能删除
* 插入表情到文字中间
需要获取光标, 虽然有 insertText: 但是只能插入 Text, 不是富文本. 但是我们发送给新浪的数据又是字符串的, 所以我们应该是插入文本, 解析字符串中的 chs, 来显示成图片,
先拿到当前的文本, 要通过正则表达式来匹配出其中的 chs, 不然用 range 搜范围会很麻烦
正则表达式:
> 有一定的字符串表达式, 根据规律来才能匹配一定格式的字符串
> 作用: 匹配
> 创建正则表达式 -> 设定匹配规则 -> 拿去匹配字符串 -> 返回数组, 数组.count 有值就是成功, 失败会=nil
系统自带的正则方法封装用用, 但是想抽出匹配的字段会比较麻烦, 推荐用老外的第三方.需要导入 libicu,,,,
封装了很多简单调用的方法, 可以容易获取到匹配的字符串, 还能获取根据匹配规则来分割的字符串
attributeString 有获取当前光标选中范围的方法, 还可以插入到某个范围, 完了后把富文本传回给 textVIew 的富文本属性
发送微博时发送的是字符串, 不是附件图片, 所以应该把文本框中的富文本中的附件图片转成对应的描述文字, 再发送
* 用富文本自带的方法遍历其自身中的每个区域, 会用字典传出, 并且带有范围,
* 用一个普通可变字符串拼接其所有转换的字符串返回
* 取出附件字典对应的值, 空则为普通字符串, 直接拼接, 非空就取出附件, (我们重写附件让其带有 emotion 属性) , 拿到 emotion.chs 拼接到字符串
* 或者这个 textView 再设置一个 text 属性, 没词点表情时在这个里面保持纯文本
显示表情在其他 Label 中, 即将里面的表情描述转换成富文本显示出来
* 这么弄的话显示出来的肯定是富文本, 那我们直接展示富文本到 Label 或其他地方上,
* 可以在模型中添加一个富文本属性, 当 cell 拿到模型直接设置富文本上去.
* 当转换到模型时设置 setText方法, 里面将纯文本转换为富文本.
解析文本为富文本
* 用正则遍历匹配字符, 再遍历正则分割的字符串,这样就能拿到原文本中整个文本分割后的纯文本和表情描述.
* 拿到文本的直接拼接进富文本, 插入到rang.loc 位置,长度是 loc+len, 表情的话找到对应的表情,拿到 attach,插入 rang.loc,len=1, 但这个应该有一个问题, 当插入的位置是当前 range 中不存在的时,可能会有数组越界错误
* 或者,保险一点的 将匹配遍历出来的文本和图片描述都保存到一个对象模型 result 中, 在通过对数组进行位置判断的排序, 得出元素按照位置重写排列好的数组, 再遍历数组,是文本的话就拼接富文本, 图片的话要遍历 emotions 模型,看看是那个图片的描述,取出对应的模型,拿到图片弄给 attach
显示@...#...#等关键字高亮
* 这些关键字必然存在存文本里, 所以在之前将 string 分割后, 得到的数组中遍历里面result.string, 当每个元素 string 中存在正则匹配时,转成的富文本中将对应 range 的文字设置属性为蓝色, 或者好像可以设置连接属性
* 其他的关键字匹配也是用这种方式
* 注意: 富文本的文字大小哟给你同样的 font计算出来可能 和纯文本的不太一样, 导致显示在用 font 计算出来的 label 里显示不全
bug: 当收到的 emoji 中有不能识别的 emoji 时, 同时还带有中文紧贴着,如😍✌🏻️✨在 xcode 中和在这里显示不了那个未识别的字符,隐藏了, 调用 boude…计算 rect 时,会报错访问坏内存. 和中文隔空格,或在未识别的 emoji 前或后隔空格,可以正确通过, 本身打印没问题,主要是调用方法上有问题
估计是在 xcode 的编码中的设置问题. 或者是NSString 在转码的时侯和调用 bounds…的时候有点问题
小问题: 长按最近表情, 会有隐藏的表情弹出来: —- 长按后的判断条件加一个非隐藏才做反应
处理关键字点击事件
* 自定义控件来显示文字并处理关键字的点击事件,
* 将显示文本的 textView 作为控件的属性, 里面提供了一个方法来获得某个 range 的范围
* 封装在控件里而不是继承可以不将 textView 的接口暴露出来, 不让别人对其设置
* 尝试下有没有属性可以设置 textView 的内切设置为毫无内切, 即便是0000也没有, 注意是 textView 的 containerInset, 不是 UIView 的 ContentInset
* 设置 textView.userInteracte = NO; 但是可能会有显示不了某些控件属性的问题,需要设置其他的禁止滚动和禁止编辑才可以减少这种情况的发生
* 实现这个自定义控件的触摸方法, 拿到当前触摸点, 判断触摸点与关键字所在的范围是不是相交, 相交的话就高亮并跳转
* 拿到当前点击 View 中关键字所在范围:
> 因为 TextRange 是抽象类, 不能直接用, 我们可以先选中某 range 文字 -> textView.selectedTextRange拿到选中文字的 textRange -> selectedRect...TextRange拿到选中文字的 rect -> 看看是否相交, (有时候会计算出宽 or 高为空的情况, 这种要进行排除)
> 要这样, 首先要知道那些需要选中的文字: 关键字, 拿到关键字:在正则匹配的时候, 多设置一个属性(可以自定义key)来保存关键字的位置和文字, 之后拿到这个属性的时候, 对应的会有这个属性设置文字的 range, 拿到 range 就可以进行上面的步奏
* 设置一个数组属性, 将当前控件中属性文本中的所有链接保存起来, 不要重复遍历寻找, 链接对象要在这里算出 rect, 不能在 status 模型中国年算, 模型中不能根据当前 label 控件算出对应的 rect 值. 在重新设置了属性文本后, 要把数组 nil, 因为里面的链接已经不一样了
* 当点击时, 遍历数组, 看看有没有和其中连接中的 rect 相交的, 找到选中链接 -> 将选中链接中遍历 rect 设置上背景
* 在结束点击抬起手的时候移除背景: 在 touch end 方法中遍历数组, 将其中的所有 rect 设置空背景,或者将选中连接中的 rect 清空或者将设置的背景保存起来, 还可以将背景 view 设置 tag, 移除时遍历 self.subViews 找 tag 来移除,结束点击的时候移除. 触摸取消时也要移除
* 当触摸 end 时, 判断触摸是否还在链接上, 是的话就发送通知告诉外界链接被点击了, 让控制器好做事.
* 控制器监听通知, 链接被点击时, 判断链接是 http 的话就用应用打开直接打开,会跳转到 Safari, 不是的话就用可能是跳转控制器, 以后处理
* 话题和@的链接可以用新浪的接口, 通过控制器展示.
发现和我的事件处理
* 因很多个控制器的界面差不多, 仅有几点控件和数据不同, 可以做一个父控制器, 共同拥有的元素放父控制器里, 再子类化出具体功能的控制器,
* 数据模型也要进行子类化, 父模型决定一样的东西, 子类模型决定模型的类型和特有的东西, 分类处理可以功能清晰, 逻辑清楚
* 模型分类使用子类化, 辅助视图判断模型的类型来做区分显示不同的控件
* 数量显示: 当优先判断 cell 模型的 bageValue, 有就显示, 没有再做类型的判断
* 最后一个退出标红的比较特别, 可以直接用 footerView 放个按钮.
* cell 的背景设置: 一般来说应该提供4种背景, 有四种不同情况的 cell, 第一行, 最后一行, 中间的那些行, 仅有一行
* item模型中的属性可以设置一个类属性, 即这个item要跳转的类
* 还可以设置一个block的属性, 即这个item点击候要进行的操作
bug: url 的匹配不正确, 当 url 后面跟着字词之后不会隔开, 会被误视为 URL 的一部分— 找另一个正则来替代
coreText 初试
用显示 string 做例子, CT 的方法很麻烦…..
147.72.4.211
微博详情页
可以将 cell 中的 detailView 拿给详情控制器的 headerView 用
将不同的东西做一个分类判断决定要不要显示.
传入模型 frame, frame 中加入对多出来控件的计算
可以将 toolbar 共同的东西抽取到一个父类里, 不同的东西在自己设置, 和对 StatusToolbar 的设置差不多
根据是否显示在详情页来显示详情的 toolBar, 这个 toolbar 可以作为转发 view 的子控件. 详情中有三种 toolbar,
API 获得某条微博的评论数,转发数, 显示在评论的 tableView 里,
每个 toolBar 的功能都很像, 获取所在微博的三个数字来显示出来,
topToolBar 在获取用户评论的时候还能获取总数
当被点击时, label会拦截点击事件来判断是否点击到链接, 这样没点击到的时候就会判断, 我们在判断完都不相交的时候发出通知被点击
应用发布与打包测试
未做
#未做: 将发图片的操作封装到工具类中
bug(访问 http 出错): 用xcode7之后, 会默认拒绝自认证的链接, 导致微博项目不能访问授权页面, 在info.plist中加入:
或者想添加例外或者更多连接网络方面: http://stackoverflow.com/questions/31216758/how-can-i-add-nsapptransportsecurity-to-my-info-plist-file/31629980#31629980
SQLite
* 当做离线缓存的时候或者保存数据到沙盒中时, 之前学的 NSKeyedArchive/NSUserDefault/Plist都不适合存储大批量的数据
* 用在手机上等嵌入式系统的应该用轻量的和快速的, SQLite就很适合,
* 用SQL语言写出来的SQL语句来在代码中在内存中操作数据库
* 本质上讲, sqlite是无数据类型的, 就算设定来数据类型还是会存什么都可以,但是数据类型显示可以比较规范, 易于沟通
* 数据定义语句: DDL>>用来创建和删除表: create if_Not_exsist 表名 | drop if_exsist 表名
* 数据操作语句: DOL>>用来对数据表中的数据进行增删改查的操作(CURD)
insert into 表名 (字段) value(值)
drop
* 改删操作没有条件限制的话会对整个表格操作, 改就全改完, 删就全删完
* 条件语句:where : 当> 给前面的改删添加限制条件, 限制记录. where 字段 = 值,
* 查询语句select 运用最多的SQL语句, select (字段(*表示通配) 字段) from 表名
* 排序, orderby 字段 升序降序
* 在iOS中使用和操作数据库
* 增加表字段: ALTER TABLE 表名 ADD COLUMN 字段名 字段类型
* 删除表字段: ALTER TABLE 表名 DROP COLUMN 字段名
* 修改表字段: ALTER TABLE 表名 RENAME COLUMN 旧字段名 TO 新字段名
> 导入libsqlite.dylib库, import主头文件, 就能使用其中的所以函数
> 创建数据库, 传入操作句柄和文件路径名
> 使用执行语句exec可以执行任何sql语句,拿来创表
> 调试技巧: __LINE__ : 表示这个所在的行, __FILE__当前所在的文件
> 模糊查询: 调用查询语句, 将要查询的字符串搭配通配符来查询
> 纯C的函数写起来比较复杂, 可以将这些操作封装在工具类中, 方法根据需要传入参数和返回类型. 根据外界沟通的必要来设置就行, 不必要就封装, 不一定要每个参数都暴露在外界. 对于内部重复利用的C代码, 看情况来封装.
试试查询语句在nativeCat上怎么样, 可以, 就是用错来like成=
实现模糊搜索 使用like而非=
直接删除记录的话再添加新的记录上去, 自增的主键id会跟着之前的id增加, 因为记录id的文件还在, 要像重新开始, 要drop原来的表, 再创新表再插数据, 才会从1开始
FMDB的基本使用
* 里面所有除查询的操作都视为update, 用upte来执行sql语句
* 查询会返回一个result, 通过result next来获取查询到的每行数据, 里面可以用int/text forColum:来获取某个字段值
* 使用线程安全的数据库: 用fmdabQueue 对象
* 传入一个路径文件, 会自己会创建一个数据库, 这个数据库是线程安全的, 提供方法来对这个数据库进行线程安全的操作, 并不能直接拿到这个数据库
* 在block中对提供的db操作是线程安全的
* 即可认为是拿到来操作这个数据库的队列来操作数据库
事务
有时候操作数据库时, 需要多条都执行成功才算是正确, 但是有时候执行到一半就因谋些问题断掉了, 这样就造成来数据错误或丢失.
这就要有个事务的概念, 当事务中的事情都完成时, 才算是完成事务, 要是美完成, 就回滚到原来的状态, 数据不会丢失.
在FMDB中begin transaction / commint transaction 来包装一个事务, 事务没有完成会自动回滚.她封装来这两个代码, 直接begin和commit就行
我们也可以在中途自己判断错误手动回滚. rollback
使用数据库完成微博的离线缓存
* 因为之前已经把加载微博数据的代码封装在业务类里, 我们只需要改动返回微博数据的业务类对应的方法即可.
* 先从沙河中加载数据: 判断, 有取到数据:显示 ? 没有取到:发送请求加载并保存到数据库, 存入的是字典
* 缓存方案, 主键id, 微博用户标识:access_token, 微博标识:id_str, 微博:statuse_dict
* 字典直接存入的话会被转换成字符串, 应该先把字典转成二进制数据, 然后在存入数据库, 取出数据库的二进制, 再转回字典. 字典转模型添加到数组返回
* ????能不能把模型直接存进去, 取出模型也直接一点 //// 最好不要, 要用data'存取, 还要在模型中coder和decoder, 属性一朵就回很麻烦!!
bug: 排序查询错误: desc asc排序要在查完表后首先做, 然后才是where来筛选
清除图片缓存
* 直接在设置里添加一行, 点击即清除缓存, 行中显示缓存大小
* 拿到缓存大小: 首先要拿到文件路径, 通过SDWebImage可以拿到她缓存文件的路径, 再用这个路径, 通过fileManager来获取其中的所有子路经,拼接出文件夹中所有文件的全路径, 计算文件的大小并加起来, 就是总大小. 返回显示.
* 有时候子路经也有可能是文件夹, 要做点判断: 传路径来, 判断是否存在, 不存在直接返回0, 存在判断是否文件夹, 文件就返回大小, 文件夹就递归调用
* 可以将这个计算路径大小的方法写成分类
* 清除缓存就直接拿到这个缓存文件夹删除就可以, 之后重新缓存的时候会再创建
* 可以把中介弱指针的创建方法写成宏来创建弱指针
建议: 不要使用宏和pch文件了. 每个.m文件中包含所需的文件, 这样耦合减小, 编译加快. 要是嫌太烦可以手动添加pch文件. 建议用静态常量替代宏
4.) 找到 Project > Build Settings > 搜索 “Prefix Header“;
5.) “Apple LLVM 7.0 -Language″ 栏目中你将会看到 Prefix Header 关键字;
6.) 输入: TestDemo/TestDemo-Prefix.pch (如 TestDemo/TestDemo-Prefix.pch );
7.),将Precompile Prefix Header为YES,预编译后的pch文件会被缓存起来,可以提高编译速度。效果如下