前言
知識是無窮無盡,技術需要積累,記錄一點一滴,讓成長的時間軸上變得充實一些。 今天就講講UITableView/UICollectionView的一些使用技巧。結合自己專案情況進行展開。
Header/Footer高度、懸停設定
高度設定
有時候我們需要設定 TableView 的頭部和尾部的間距,頭部尾部的高度只需要在代理裡面設定高度就行。
示例程式碼如下:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.01f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 20)];
view.backgroundColor = [UIColor clearColor];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 10;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 1)];
view.backgroundColor = [UIColor clearColor];
return view;
}
複製程式碼
小結:通過上面的程式碼實現的效果可以看出,當自定義頭部和尾部檢視時,即使自定義頭部和尾部檢視設定了高度。高度最終還是由tableView:heightForHeaderInSection:
和tableView:heightForFooterInSection:
兩個代理方法決定。當沒有實現這兩個代理方法時,高度為預設高度。
懸停
TableView的懸停功能只有在 Style
是 UITableViewStylePlain
的時候才有。如果有這麼一種需求,就是需要有Header懸停,同時每個section之間需要有間隔。
效果如下:
方式一(全部懸停):
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return kHeaderHeight+9;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
BillListModel *model = _sectionArr[section];
UIView *header = ({
UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, kHeaderHeight+9)];
bgView.backgroundColor = [UIColorTools colorWithTheme:UIColorThemeAppBgColor];
UILabel *titleLab = [[UILabel alloc] initWithFrame:CGRectMake(0, 9, KScreenWidth, kHeaderHeight)];
titleLab.backgroundColor = [UIColorTools colorWithTheme:UIColorThemeWhite];
titleLab.textColor = [UIColorTools colorWithTheme:UIColorThemeBlack];
titleLab.text = model.time;
[bgView addSubview:titleLab];
UIImageView *lineHBottom = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, titleLab.mj_max_y - 0.5, KScreenWidth, 0.5)];
lineHBottom.backgroundColor = [UIColorTools colorWithTheme:UIColorThemeSeparatorColor];
[bgView addSubview:lineHBottom];
bgView;
});
return header;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == _tableView) {
UITableView *tableview = (UITableView *)scrollView;
CGFloat sectionHeaderHeight = kHeaderHeight;
CGFloat sectionFooterHeight = 9;
CGFloat offsetY = tableview.contentOffset.y;
if (offsetY >= 0 && offsetY <= sectionFooterHeight) {
tableview.contentInset = UIEdgeInsetsMake(-offsetY, 0, -sectionHeaderHeight, 0);
} else if (offsetY >= sectionFooterHeight && offsetY <= tableview.contentSize.height - tableview.frame.size.height - sectionHeaderHeight) {
tableview.contentInset = UIEdgeInsetsMake(-sectionFooterHeight, 0, 0, 0);
} else if (offsetY >= 0 && tableview.contentSize.height >= tableview.contentSize.height) {
tableview.contentInset = UIEdgeInsetsMake(-sectionFooterHeight, 0, 0, 0);
}
}
}
複製程式碼
方式二(部分懸停):
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section == 0) {
return 10.f;
} else if (section == 2) {
return 0;
}
return kHeight4_7(35);
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 10.f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [UIView new];
headerView.backgroundColor = [UIColorTools colorWithTheme:UIColorThemeWhite];
if (section == 0) {
headerView.backgroundColor = [UIColor clearColor];
}
return headerView;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *footerView = [[UIView alloc] init];
footerView.backgroundColor = [UIColor clearColor];
return footerView;
}
複製程式碼
CollectionView的懸停功能只有在 Style
是 UITableViewStylePlain
的時候才有。如果有這麼一種需求,就是需要有Header懸停,同時每個section之間需要有間隔。
效果如下:
滾動監聽
UIScrollView 減速
可能通過decelerationRate的屬性來設定,它的值域是(0.0,1.0),當decelerationRate設定為0.1時,當手指touch up時就會很慢的停下來。
UIScrollView 如何判斷停止滑動
這裡停止滑動的意思要明確一下,有兩種:
1、第一種是指手指停止ScrollView。
當手指停止滑動時,iOS會調UIScrollView的delegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
複製程式碼
如果decelerate還為NO時,它最終停下,否則它還沒最終停下
2、第二種是指ScrollView停止滑動,指的滾動條完全停止下來。
當decelerate = true時,iOS才會調UIScrollView的delegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
複製程式碼
那UIScrollView真正停止滑動,應該怎麼判斷呢? 解決方法如下:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if(!decelerate) {
//OK,真正停止了,do something
}
}
//然後
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
//OK,真正停止了,do something
}
複製程式碼
UIScrollView左右滑動到某個位置時,禁止繼續向左或者向右滑動
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat contentOffsetX = scrollView.contentOffset.x;
if (contentOffsetX<=0 || contentOffsetX>=kScreenWidth) {
//當滑動到最左邊或者最右邊時,禁止繼續滑動
scrollView.scrollEnabled = NO;
} else {
scrollView.scrollEnabled = YES;
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
scrollView.scrollEnabled = YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
scrollView.scrollEnabled = YES;
}
複製程式碼
訊息傳遞
當手指觸控後,scrollView會暫時攔截觸控事件,使用一個計時器,假如在計時器到點後,沒有發生手指移動事件,那麼,scrollView傳送tracking events到被點選的subView。 假如在計時器到點前,發生了移動事件,那麼scrollView取消tracking自己發生滾動。
子類可以過載touchesShouldBegin:withEvent:inContentView:
決定自己是否接收touch事件。
當pagingEnabled
值為YES
,會自動滾動到subView的邊界,預設是NO
。
touchesShouldCancelInContentView:
開始傳送tracking messages訊息給subView的時候
呼叫這個方法,決定是否傳送tracking messages訊息到subview,假如返回NO
,則傳送,YES
則不傳送。
假如canCancelContentTouches
屬性是NO
,則不呼叫這個方法來影響如何處理滾動手勢。
修改tableView中headerView的位置(類似美團外賣首頁)
實現原理就是監聽滾動情況,重設scrollView.contentInset即可 效果如下:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat contentOffsety = scrollView.contentOffset.y;
_contentOffSet_y = contentOffsety;
//這個header其實是section的header到頂部的距離
CGFloat header = kBannerHight+[HSFuntionCell cellHeight]+kFooterViewHeight-64;
NSLog(@"=======%lf=====%lf", contentOffsety, header);
if (contentOffsety<=header&&contentOffsety>=0) {
//當檢視滑動的距離小於header時
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
self.headerView.layer.borderColor = [UIColorTools colorWithTheme:UIColorThemeWhite].CGColor;
} else if (contentOffsety>header) {
//當檢視滑動的距離大於header時,這裡就可以設定section的header的位置,設定的時候要考慮到導航欄的透明對滾動檢視的影響
scrollView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
self.headerView.layer.borderColor = [UIColorTools colorWithTheme:UIColorThemeSeparatorColor].CGColor;
}
self.headerView.borderWhich = DDViewBorderTop;
//設定導航條透明度
[self setNavigationColor:contentOffsety];
}
複製程式碼
頂部拉伸效果(頭像拉伸)
實現思路:自定義一個ViewA,作為TableView的headerView,然後監聽TableView的滾動,將回撥傳遞給ViewA即可。
效果如下:
下面是自定義MOActivityTopView
.h檔案
@interface MOActivityTopView : UIView
@property (nonatomic, strong) MOActivityModel *model;
- (void)didScroll:(CGFloat)contentOffSetY;
@end
複製程式碼
.m檔案
#import "MOActivityTopView.h"
#define kViewHeight (kScreenWidth*340/750.)
#define kTopHeight (kScreenWidth*240/750.)
#define kBottomHeight (kScreenWidth*100/750.)
@interface MOActivityTopView ()
/// 背景圖
@property (nonatomic, strong) UIImageView *backgroundImgV;
/// 毛玻璃
@property (nonatomic, strong) UIVisualEffectView *visualEffectView;
/// 活動圖
@property (nonatomic, strong) UIImageView *activityImgV;
/// 活動名稱
@property (nonatomic, strong) UILabel *activityLab;
@end
@implementation MOActivityTopView
- (instancetype)init {
if (self = [super initWithFrame:CGRectMake(0, 0, kScreenWidth, kViewHeight)]) {
[self setUp];
}
return self;
}
#pragma mark - Getter
-(UIImageView *)backgroundImgV {
if (_backgroundImgV == nil) {
_backgroundImgV = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.dd_w, kTopHeight)];
[_backgroundImgV setContentMode:UIViewContentModeScaleAspectFill];
[_backgroundImgV setClipsToBounds:YES];
}
return _backgroundImgV;
}
- (void)setUp {
[self addSubview:self.backgroundImgV];
UIVisualEffect *blurEffect;
blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
// _visualEffectView.alpha = 0.8;
_visualEffectView.frame = self.backgroundImgV.frame;
[self addSubview:_visualEffectView];
UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(0, self.backgroundImgV.dd_max_y, kScreenWidth, kBottomHeight)];
bgView.backgroundColor = [UIColorTools colorWithTheme:UIColorThemeWhite];
[self addSubview:bgView];
CGFloat height = (kScreenWidth*160/750.);
CGFloat width = height/(16/21.);
_activityImgV = [[UIImageView alloc] initWithFrame:CGRectMake(15, bgView.dd_h-7-height, width, height)];
[bgView addSubview:_activityImgV];
_activityLab = [[UILabel alloc] initWithFrame:CGRectMake(_activityImgV.dd_max_x+8, 0, kScreenWidth-_activityImgV.dd_max_x-8-15, kBottomHeight)];
_activityLab.textColor = [UIColorTools colorWithTheme:UIColorThemeBlack];
_activityLab.numberOfLines = 2;
// _activityLab.adjustsFontSizeToFitWidth = YES;
[bgView addSubview:_activityLab];
}
- (void)layoutSubviews {
[super layoutSubviews];
_activityImgV.clipsToBounds = YES;
_activityImgV.layer.masksToBounds = YES;
_activityImgV.layer.borderWidth = 1.5;
_activityImgV.layer.borderColor = [UIColorTools colorWithTheme:UIColorThemeWhite].CGColor;
_activityImgV.layer.cornerRadius = kViewCornerRadius;
}
- (void)setModel:(MOActivityModel *)model {
_model = model;
[_activityImgV sd_setImageWithURL:kMOImageUrlSet(model.ActivityURL) placeholderImage:[UIImage placeholderImage_activity]];
[_backgroundImgV sd_setImageWithURL:kMOImageUrlSet(model.ActivityURL) placeholderImage:kImageSet(@"Icon-noti")];
_activityLab.text = model.ActivityName;
}
- (void)didScroll:(CGFloat)contentOffSetY {
//圖片高度
CGFloat imageHeight = self.dd_h;
//圖片寬度
CGFloat imageWidth = kScreenWidth;
//圖片上下偏移量
CGFloat imageOffsetY = contentOffSetY;
// NSLog(@"圖片上下偏移量 imageOffsetY:%f ->",imageOffsetY);
//下拉
if (imageOffsetY < 0) {
CGFloat totalOffset = imageHeight + ABS(imageOffsetY);
CGFloat f = totalOffset / imageHeight;
self.backgroundImgV.frame = CGRectMake(-(imageWidth * f - imageWidth) * 0.5, imageOffsetY, imageWidth * f, totalOffset);
}
// //上拉
// if (imageOffsetY > 0) {
// CGFloat totalOffset = imageHeight - ABS(imageOffsetY);
// CGFloat f = totalOffset / imageHeight;
// [self.backgroundImgV setFrame:CGRectMake(-(imageWidth * f - imageWidth) * 0.5, imageOffsetY, imageWidth * f, totalOffset)];
// }
_visualEffectView.frame = self.backgroundImgV.frame;
}
@end
複製程式碼
監聽滾動
- (UIView *)topHeaderView {
if (!_topHeaderView) {
_topHeaderView = [[MOActivityTopView alloc] init];
_topHeaderView.model = _model;
}
return _topHeaderView;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat contentOffSetY = scrollView.contentOffset.y;
[self.topHeaderView didScroll:contentOffSetY];
}
複製程式碼
再一次感謝您花費時間閱讀這篇文章!
微博: @Danny_呂昌輝
部落格: SuperDanny