从2月12日开始到3月9日,一个月学习这个项目收获了很多知识点,给自己总结出来,方便以后查看。
-
1.控制器的view加载完毕,首先在
- (void)viewDidLoad
方法中添加 下拉刷新 的控件;- 1.1将添加下拉刷新控件封装一个函数
- (void)setupRefresh
,该方法中添加MJRefresh框架的header控件 - 1.2写一个头部控件调用的刷新方法
- (void)loadNewTopics
,里面写发送网络请求代码
- 1.1将添加下拉刷新控件封装一个函数
-
2.在成功返回的block中调用endRefreshing将Header控件隐藏起来,当然失败一样也要影藏起来;
-
3.一进来TableView就应该自动刷新,所以在
- (void)setupRefresh
方法中添加完header控件,就调用它的beginRefreshing
方法,让其自动刷新; -
4.利用header的
automaticallyChangeAlpha(YES)
属性自动改变header的透明度,让其影藏,解决下图效果问题
-
5.利用
MJRefreshAutoNormalFooter
类添加footer控件,一开始设置footer控件hidden属性为YES隐藏;- 5.1MJRefreshAutoNormalFooter类的footer是上拉到指定位置自动刷新
- 5.2 MJRefreshBackFooter类刷新完会有一个回弹的效果
-
6.上拉刷新加载更多数据,所以将下拉刷新发送网络请求的代码同样复制一份到 - (void)loadMoreTopics方法中,但是加载更多需要下一个的页码参数
网络延迟中一些细节处理
-
7.网络不好时,上拉加载更多失败了,但是page++了,下次加载就会直接跳过失败那页数据,所以在失败的block中将page—;
-
8.加入显示处在第5页数据中,先在下拉刷新加载最新数据,但是也有可能失败,一旦下拉刷新page被清空为0了,但现在处在第5页,如果现在上拉刷新加载更多,将会重复加载第0页数据。所以改进办法就是当下拉刷新成功了,才将页码清空为0,将self.page = 0; 放到成功的block中;
-
9.用户有可能下拉刷新又上拉刷新 例如:现在用户当前处在第5页,先下拉刷新,又上拉刷新,一会下拉刷新的数据回来后,page被清空为0了,之后上拉数据回来,成功后会将第六页数据放到第5页数据后,但是第5页数据被清空了,就讲最前面的0页数据和第6页数据合在一起了。如果上拉数据失败,page—,这样当前page被减为负数,也是有问题。 *两种解决办法:
- (1)控制器增加一个字典成员属性params,只处理最后一次的网络请求,如果当前返回的请求和params中的请求不是同一个请求,直接return
if(self.params != params) return;
复制代码
-
(2)禁止用户同时上拉和下拉刷新,如果发现有一个在刷新,则禁止掉例外一个刷新
-
10.footer控件永远粘着cell的底部,如果一上来,TableView没有数据,那么会直接显示footer控件。所以创建完footer控件时,默认让其隐藏,同时在
numberOfRowInSession
中设置如果返回的count==0时隐藏复制代码
self.tableView.mj_footer.hidden = (self.topics.count == 0);
- 11.设置TableView内边距的一些代码放在“精华”控制器中不合适,应该放到TableView自己的控制器中,子控制器的代码应该放到子控制器
- 12.在other文件中新建一个保存常量的类(继承NSObject),用来保存一些全局都用到的常量信息
- 12.1 删除所有内容,导入.h和.m都导入 #import <UIKit/UIkit.h>
- 12.2 .m中放一些const常量,.h中放一些放常量的引用同时加上 UIKIT_EXTERN
- 13.修改TableView内部的Cell的尺寸,让其左右有间距,需要重写他的`setFrame`方法,只需要设置一次的东西就写在`awakeFromNib(xib)`,或`initWithFrame`(代码)方法中
- 14.将createTtime字符串时间转变为`NSDate`格式的时间
```objc
NSDate *now = [NSDate date]; // 当前时间
NSDateFormat *fmt = [[NSDateFormat alloc] init];
fmt.dateformat = @“yyyy-MM-dd HH:mm:ss”;
NSDate *createtime = [fat dateWithString:topic.create_time];
复制代码
- 15.两个时间的比较方法
- (1):返回一个时间间隔(s)
NSTimeInterval delta = [now timeIntervalSinceDate:create];
复制代码
- (2):NSCalendar类,有一个currentCalendar方法获取当前的日历
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger year= [calendar component:NSCalendarUnitYear fromDate:now];
复制代码
其中NSCalendarUnitYear
是一个枚举,里面有年、月、日、时、分、秒components:fromDate:同上一样,可以传多个枚举值,返回一个NSDateCompoents
,里面放着年、月、日、时、分、秒 时间的比较:
NSDateCompoents cmps =[calendar components:枚举 fromDate:create toDate:now options:0]
复制代码
cmps.year, cmps.month等就是相差的间隔。
-
16.UITableView的Cell有一个
TableView:heightForRowAtIndexPath:
方法,直接动态计算出每一个Cell的高度,但是这个方法调用非常频繁,所以将具体计算代码放在这里会影响性能。应该给模型提供一个cellHeight属性,重写该属性的getter方法,将计算的代码放这里。并且加一个if判断,如果_cellHeight没有值,才进行计算,这样就保证只计算一次,提升性能。但是提供这样一个属性暴漏在外面,别人很有可能会修改属性,所以加上readonly。 -
17.如果一个成员变量加了readonly,又重写了getter方法,那么getter方法中的成员变量就会报错,因为编译器认为有readonly的只生成getter方法,自己又实现了getter方法,意味着成员变量也需要自己实现,所以报错。这时,就需要我们自己再实现成员变量,在.m中加上_cellheight的成员变量。
-
18.
- boundingRectWithSize:options:attributes:context:
方法是根据文字的大小、宽度,动态计算出这段文字的高度。 -
19.iPhone默认是不支持gif图片播放的,必须将一个gif图片解析成n个UIImage对象才能进行播放,用到ImageIO框架
-
20.imageView的contentMode属性中有一个UIViewContentModeScaleToFit属性,表示,缩放图片比例,在现有的显示尺寸的大小内,保证看到图片的全部。另外
UIViewContentModeScaleToFill
模式是,等比例伸缩,然后在现有显示尺寸的大小内,显示伸缩后的图片的中间部分,但是剩余的头尾剩余部分也会显示出来,会遮盖其他控件,所以在显示图片的iamgeView右侧栏中间的模式中,勾选Clip Subviews即可将多余的部分裁剪掉。 -
21.带有图片的段子,为了提高用户体验,在图片没有下载完之前,
- 1.先显示占位图片;
- 2.提供一个下载的进度条;
-
22.屏蔽第三方框架带来的风险,就是自己建立一下类,然后继承自框架的类,这样面向自己类做开发,以后如果不用这个框架了只需要改这个类即可,这样减少不用框架后多处修改代码的风险
-
23.
-(void)UIImageWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SELcompletionSelector, void *contextInfo );
用来将图片保存进用户的相册,第一次调用会弹框询问。 -
24.图片段子中的图片,自定义一个UIView类,继承UIView,提供一个类方法返回一个创建好的UIView, ```objc [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
- 25.根据模型的内容(图片、声音or视频?),将UIView添加到对应的cell中,所以在模型属性的setter方法中写;
- 26.为了保证只创建一次UIView,Cell来回滚动时可以循环利用,所以采用懒加载,但是控件都是弱指针,代码段执行完控件就死了,所以在懒加载中直接将控件下到contentView中
- 27.图片段子中的图片,需要给自定义的图片View增加一个topic模型,然后在if判断中若为图片段子则将topic模型传递给图片view的模型中,这样view才能拿到数据,设置数据到对应的view中去显示
- 28.如果Cell的尺寸做了修改,Cell内部的子控件frame尺寸不对,则将autoresizingMask属性设置为NO
self.autoresizingMask = UIViewAutoresizingNone;
- 29.判断gif图片标志的显示或隐藏
```objc
// 判断GIF是否显示
NSString *extensionName = topic.large_image.pathExtension;
self.gifView.hidden = ![extensionName.lowercaseString isEqualToString:@"gif"];
复制代码
-
30.在不知道图片扩展名的情况下,SDWebImage框架中有一个方法,可以取出图片数据的第一个字节,进行判断,获得图片的扩展名,这种方式是最准确的
-
31.发布按钮点击后,一进来,几个button在执行动画的过程中,应该让按钮不能被点击,所以在控制器的
viewDidLoad
方法中设置self.view.userInteractionEnabled = NO;
(不响应用户交互事件),动画执行完毕(最后标语落下来时),在setCompetionBlock
这个block中恢复view的点击事件; -
32.UIWindow有三个级别,Normal < StatusBar < Alert 依次升高。如果想做一个动画,在状态栏提示一些东西,就创建一个Window,设置为状态栏级别,挡住系统自带的状态栏,因为状态栏就是一个StatusBar级别的window。
-
33.命名规范
- (1)成员变量访问以下滑线开头;
- (2)全局变量可以在名称后面加一个下划线与成员变量区分;
- (3)如果没有任何东西就是局部变量
-
34.发布按钮,一进来动画在执行的过程中,让下降的按钮应该不能被点击,所以在
viewDidLoad
中让view.userInteractionEnabled = NO
,在动画执行完毕后再恢复能点击,在 setCompletionBlock:^的block中恢复 -
35.
reloadData
刷新表格时就会调用数据源的三个方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
复制代码
-
36.header和cell一样也可以循环利用
dequeueReusableHeaderFooterViewWithIdentifier:
利用这个方法从缓存池中找这个标识的headerfooterView
,返回一个UITableViewHeaderFooterView
-
37.从iOS8开始提供了一个自动计算Cell高度尺寸的属性,先给出估算高度,然后让真实高度根据具体尺寸伸缩
self.tableView.estimatedRowHeight = 44;
self.tableView.rowHeight = UITableViewAutomaticDimension;
复制代码
-
38.连续点击两次tabBar自动刷新当前窗口中的TableView,是有通知完成,系统的delegate成为tabBarController的代理,当tabBar被点击时,发出一个通知。对应的TableViewController接到通知,调用自己的头部控件开始刷新
-
39.UIView没有图片属性,也没有背景图片属性,所以要给UIView添加背景需要调用drawRect:方法画上去
-
40.监听textFiled的文字改变可以用三种方法:
- (1)代理 - 成为代理——>遵守协议——>实现方法 (不推荐)
- (2)通知 有一个UITextFiledDidChangeNotification
- (3)addTarget textFiled继承自UIControl
-
41.从iOS7以后
[[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStyleDone target:self action:@selector(cancel)];
复制代码
style样式写什么都一样
- 42.设置导航控制器view上的标题文字大小的两种方式
- (1)UIViewController被导航控制器包装后拿到titleView属性,自己包装一个UIView,里面放一个Label将文 字调整一下 self.navigationItem.titleView
- (2)拿到 navigationBar属性,在navigationController的initialize方法中设置
[self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:20]}];
复制代码
- 43.如果在navigatioController利用appearance分状态修改了
UIBarButtonItem
的属性,那么在控制器中设置rightBarButtonItem
状的enabled的属性之后,要调用layoutifNeeded
函数强制刷新
[self.navigationController.navigationBar layoutIfNeeded];
复制代码
-
44.UITextView没有占位文字,可以自定义一个继承自UITextView,增加一个placeholder属性的UITextView,方法有两种:
- (1)实现 - (void)drawRect:(CGRect)rect 将文字画上去
- (2)给TextView增加一个子控件Label,来显示占位文字,好处是,子控件可以在textView实现滚动效果
-
45.监听UITextView的文字改变有很多中方法
- (1)通过代理,有一个
TextViewDidChange:
方法可以监听文字的改变,但是不推荐这种方法,因为控制器要成为代理来监听控件的改变,高耦合性,占位文字是控件内部的功能,应该由自己来控制或隐藏 - (2)TextView文字发生改变会发出通知
UITextViewDidChangeNotification
- (1)通过代理,有一个
-
46.如果监听了通知,一定要调用dealloc方法,将监听删除
-
47.文字一旦改变就会调用监听方法,在监听方法中调用
setNeedsDisplay
方法重新绘制,因为setNeedsDisplay
会调用DrawRect:
方法,该方法每次进来会将之前的内容擦掉,重新绘制一次 -
48.
UITextView
给别人提供一个占位文字的属性,别人很有可能随时更改这个属性值,所以要重写属性的setter方法,时时监听属性的修改,需要重写以下几个方法,同时都要调用setNeedsDisplay
方法- (1) 重写占位文字的属性方法
- (2) 重写字体属性的setter方法,因为字体别人也有可能修改,同样也要调用
- (3) 别人可能通过代码修改text值,所有也要重写setText:方法(调用父类的该方法)
- (4)别人修改的也有可能是富文本属性AttributedText,所有这个属性也要重写
-
49.TextView继承
UIscrollView
,所以他是能滚动的,其中有一个属性alwaysBounceVertical
,在竖直方向上有弹簧效果 -
50.增加的label采用懒加载先确定位置,如果label的宽度确定,高度是要通过文字计算出来,所以在
layoutSubviews
方法中计算根据文字数量和字体大小确定后的label的尺寸,但是控制器中有可能随时修改这个尺寸和文字,所以要在setter方法中调用setNeedsLayout
方法在恰当的时间重新计算更新后的label的尺寸
/**
* 更新label的尺寸
*/
- (void) layoutSubviews
{
CGSize size = CGSizeMake( self.width - 2 * self.phLabel.x, MAXFLOAT);
self.phLabel.size = [self.placeholder boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.font} context:nil].size;
}
复制代码
-
51.
setNeedsDisplay
会在恰当的时候调用drawRect:
方法setNeedsLayout
会在恰当时候调用layoutSubviews
方法 -
52.通过代码设置创建按钮的尺寸和图片的尺寸一样的方法
- (1)button.size = [UIImage ImageNamed:@“tag_add_icon”].size;
- (2)取出普通状态下的图片 button.size = [button iamgeForState:UIControlStateNormal].size;
- (3)获取正在展示的图片 button.size = button.currentImage.size;
-
53.一旦根控制器modal出一个控制器,就会用presentedViewController属性引用着它。
- a.
presentedViewController
-> b a控制器的这个属性可以找到弹出的b控制器 - b.
presentingViewController
-> a b控制器的这个属性可以找到谁把他push出来了
- a.
-
54.监听
TextFiled
的文字改变的三种方法:- (1)代理方法,但对中文的支持不好,不推荐
- (2)通知 UITextFieldTextDidChangeNotification
- (3)addTarget:方法
-
55.按钮的
contenMode
属性用在内部的image身上,对文字的对齐方式不起作用,contentHorizontalAlignment
属性,是设置按钮内部文字或图片水平方向的排布 -
56.右下角的换行不是字符,所以不能通过
addTarget:
方法中的点击方法监听,要通过代理监听
- (BOOL)textFieldShouldReturn:(UITextField *)textField
复制代码
-
57.
- (void)deleteBackard
是监听键盘的删除按键 -
58.自己定义的Block很容易发生循环引用,所以一定要用弱引用
-
59.布局控制器view中的子控件位置和尺寸的代码最好放到
viewDidLayoutSubviews
方法中,因为控制器的view有可能是通过xib、代码、storyboard创建,所以一开始初始化时的尺寸是不准确的。但是当view布局完成后的尺寸是最准确的 -
60.
Xcode
中蓝色文件夹表示在项目中真实存在 -
61.
ABS()
是取绝对值函数 -
62.
- (void)prepareLayout
用来做布局的初始化操作(不建议在 init 方法中进行布局的初始化操作) -
63.
- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error;
这个方法获取文件大小是可靠的,文件计算不准确