又偷偷的写了一段代码。
不得不感慨,还是这种代码好写。主要是不伤脑细胞。hhhh
丰富cell的内容
因为时间隔着有些久远,所以及时找回状态呢,就是盯着第一篇文章里的效果图,把cell里面的内容先添加上去。当然这些内容是不用思考的,但有一样是要考虑的,就是添加的层次,我的思路呢就是先添加一个背景View(bgView),然后把展开后下面的那部分label放在一个titleView里面,然后整体放在bgView上,然后再把上面的图片放在上面。 出于美观的考虑,我们还可以在最下面放一层阴影视图,这样在展开关闭的时候看起来自然一下。
添加如下的属性,里面的两个集合视图是自定义的,一个用了iOS9的UIStackView,另一个用的是UIView(- -,是因为自己对stackView不大熟悉,我怕会影响后面对用户视图写动画时不方便)
///卡片控件大背景
@property (nonatomic, strong) UIView *bgView;
///图片视图
@property (nonatomic, strong) UIImageView *coverImageView;
///文本视图
@property (nonatomic, strong) UIView *titleView;
///标题
@property (nonatomic, strong) UILabel *title;
///喜爱标签
@property (nonatomic, strong) UILabel *favoriteLabel;
///星星集合视图
@property (nonatomic, strong) CardStartsView *startsView;
///用户集合视图
@property (nonatomic, strong) CardUsersView *usersView;
然后下面讲这些空间的设置添加,用了Masonry。对于Mansory,比起其他的那些轻量的自动布局的框架,功能更多一些,虽然有些繁琐,所以我们可以花时间去了解一下具体更多的用法,或者去里面观察一下内部实现。但是这里就不讲了。
//阴影视图
UIView *shadowView = [self createShadowView];
//背景视图
self.bgView = [self setViewWithColor:[UIColor whiteColor] andCornerRadius:5 andAlpha:0.0];
[shadowView addSubview:self.bgView];
[self.bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(shadowView);
}];
//文字视图
self.titleView = [self setViewWithColor:[UIColor whiteColor] andCornerRadius:5 andAlpha:1.0];
[shadowView addSubview:self.titleView];
[self.titleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(shadowView.mas_bottom);
make.left.equalTo(shadowView.mas_left);
make.right.equalTo(shadowView.mas_right);
make.height.mas_equalTo(@100);
}];
//标题
self.title = [UILabel cz_labelWithText:@"**是个大傻子 * 100" fontSize:16 color:[UIColor lightGrayColor]];
self.title.numberOfLines = 1;
[self.titleView addSubview:self.title];
[self.title mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleView.mas_top).offset(15);
make.left.equalTo(self.titleView.mas_left).offset(15);
make.width.mas_equalTo(self.contentView.frame.size.width);
}];
//喜欢
self.favoriteLabel = [UILabel cz_labelWithText:@"100 comments" fontSize:14 color:[UIColor grayColor]];
self.favoriteLabel.textAlignment = NSTextAlignmentLeft;
[self.titleView addSubview:self.favoriteLabel];
[self.favoriteLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.title.mas_bottom).offset(10);
make.left.equalTo(self.titleView.mas_left).offset(15);
make.height.mas_equalTo(@20);
}];
//评价
self.startsView = [[CardStartsView alloc] init];
self.startsView.level = 4;
[self.titleView addSubview:self.startsView];
[self.startsView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.favoriteLabel.mas_centerY);
make.left.equalTo(self.favoriteLabel.mas_right).offset(10);
make.width.mas_equalTo(@70);
make.height.mas_equalTo(@20);
}];
//用户集合视图
NSArray *arr = [NSArray arrayWithObjects:@"1", @"2", @"3", @"4",nil];
self.usersView = [[CardUsersView alloc] init];
self.usersView.users = arr;
[self.titleView addSubview:self.usersView];
[self.usersView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@30);
make.width.equalTo(@(arr.count * 25 + 5));
make.top.equalTo(self.favoriteLabel.mas_bottom).offset(3);
make.left.equalTo(self.titleView.mas_left).offset(15);
}];
//最上面的图片
self.coverImageView = [[UIImageView alloc] init];
self.coverImageView.layer.cornerRadius = 5; //切个小圆角
self.coverImageView.layer.masksToBounds = YES;
self.coverImageView.userInteractionEnabled = NO;
[shadowView addSubview:self.coverImageView];
[self.coverImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(shadowView);
}];
里面有两个封装了一下的方法,还有一些label的设置是直接用别人的一些包,当然都是对最基本功能的一个简化
- (UIView *)setViewWithColor:(UIColor *)color andCornerRadius:(CGFloat)radius andAlpha:(CGFloat)alpha
{
UIView *view = [[UIView alloc] init];
view.alpha = alpha;
view.backgroundColor = color;
view.layer.cornerRadius = radius;
view.layer.masksToBounds = YES;
return view;
}
- (UIView *)createShadowView
{
//阴影视图
UIView *shadowView = [[UIView alloc] init];
shadowView.layer.cornerRadius = 5;
shadowView.clipsToBounds = NO;
shadowView.layer.shadowColor = [UIColor colorWithRed:138.0/255 green:138.0/255 blue:138.0/255 alpha:1.0].CGColor;
shadowView.layer.shadowOffset = CGSizeMake(0, 5);
shadowView.layer.shadowOpacity = 0.8;
shadowView.layer.shadowRadius = 10;
[self.contentView addSubview:shadowView];
[shadowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentView);
}];
return shadowView;
}
添加手势
后面一定是要添加手势,有一个滑动手势,还有一个点按手势,这里我们先添加滑动手势,至于点按的手势,因为它的作用是为了引出另外一个动画,所以我们就放在后续进行说明。
这里需要说明的就是手势应该在哪份文件添加好(当然是在cell上面添加),为了简便呢,我们在cell的上一层collectionView添加,这样可以省一些两者之间的交互。因为在父类里面可以通过很多现成的方法去拿到子类的内容
其次呢就是说一个小技巧,如何对一个两份代码基本一样,但是刚好运算和一些设置是反着的代码进行封装。这个可以通过对引入一个正负一参数对里面的操作进行正负一运算就可以整合成一起了,对于设置,用这个参数进行三目运算也可以区别开来。
- (void)addGesture
{
//上滑
self.up = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeUp:)];
self.up.direction = UISwipeGestureRecognizerDirectionUp;
[self.collectionView addGestureRecognizer:self.up];
//下滑
self.down = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeDown:)];
self.down.direction = UISwipeGestureRecognizerDirectionDown;
//这里先不添加下滑手势,避免在没展开的时候,同时存在两种手势(只有在上滑以后,才会有下滑手势)
}
- (void)swipeUp:(UISwipeGestureRecognizer *)swipeRecognizer
{
[self setSwip:swipeRecognizer andDrection:Up];
}
- (void)swipeDown:(UISwipeGestureRecognizer *)swipeRecognizer
{
[self setSwip:swipeRecognizer andDrection:Down];
}
下面是setSwip的封装方法 ,在这个方法的对cell位置进行操作/获取的方法都是非常常用的。
- (void)setSwip:(UISwipeGestureRecognizer *)swipeRecognizer andDrection:(Direction)direction
{
//得到point.x位置,用以确定item是第几个
CGPoint location = [swipeRecognizer locationInView:self.collectionView];
NSIndexPath *indexpath = [self.collectionView indexPathForItemAtPoint:location];
//NSLog(@"indexpath.row : %ld",(long)indexpath.row);
//得到对应的cell
CardCollectionViewCell *swipeCell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexpath];
if (direction == Up) {
//返回cell在目标视图(collectionView)中的位置
CGRect rect = [self.collectionView convertRect:swipeCell.frame toView:self.collectionView];
//手势没在cell范围上(通过打印发现没在位置的时候,size 和 origin 都是 0
//NSLog(@"size:{%f %f} origin:{%f %f}",rect.size.width, rect.size.height, rect.origin.x, rect.origin.y);
if (rect.origin.x == 0 && rect.origin.y == 0) {
return;
}
//没有在当前手势上
if (indexpath.row != _currentIndex) {
return;
}
}
//执行UIView动画
[UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:3 options:UIViewAnimationOptionCurveEaseInOut animations:^{
//coverImage
CGRect coverFrame = swipeCell.coverImageView.frame;
coverFrame.origin.y -= 100 * direction;
swipeCell.coverImageView.frame = coverFrame;
//bgView
CGRect bgFrame = swipeCell.bgView.frame;
bgFrame.size = CGSizeMake(swipeCell.bgView.frame.size.width + 30 * direction, swipeCell.bgView.frame.size.height + 20 * direction);
bgFrame.origin.x -= 15 * direction;
swipeCell.bgView.frame = bgFrame;
swipeCell.bgView.alpha = direction < 0 ? 0.0 : 1.0;
//titleView
CGRect titleFrame = swipeCell.titleView.frame;
titleFrame.size = CGSizeMake(swipeCell.titleView.frame.size.width + 30 * direction, swipeCell.titleView.frame.size.height);
titleFrame.origin.x -= 15 * direction;
swipeCell.titleView.frame = titleFrame;
} completion:^(BOOL finished) {
self.collectionView.scrollEnabled = direction > 0 ? NO : YES;
swipeCell.coverImageView.userInteractionEnabled = direction > 0 ? YES : NO;
if (direction == Up) {
[self.collectionView removeGestureRecognizer:self.up];
[swipeCell addGestureRecognizer:self.down];
} else {
[self.collectionView addGestureRecognizer:self.up];
[swipeCell removeGestureRecognizer:self.down];
}
}];
}
两个集合视图
下面说一下那两个自定义的集合View
StackView的方便之处就在于他不需要我们进行手动的适配位置。但这样也有不方便的地方,那就是他把很多东西都固定了。所以适用范围就小。
//通过重写Level属性的set方法来对内容赋值
- (void)setLevel:(CGFloat)level
{
NSInteger startCount = (NSInteger)level;
//满星
for (NSInteger i = 0; i < startCount; i++) {
[self createStartWithImageName:@"comtent_star_icon" startPosition:i];
}
//半星
if (level - startCount) {
[self createStartWithImageName:@"yiban_star_icon" startPosition:startCount];
startCount++;
}
for (NSInteger i = startCount; i < StartCount; i++) {
[self createStartWithImageName:@"comtent_star_icon nal" startPosition:i];
}
_level = level;
}
- (void)createStartWithImageName:(NSString *)imageName startPosition:(NSInteger)position
{
UIImageView *imageView = nil;
if (self.subviews.count == StartCount) {
imageView = self.subviews[position];
imageView.image = [UIImage imageNamed:imageName];
return;
}
imageView = [[UIImageView alloc] init];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:imageName];
[self addArrangedSubview:imageView];
}
对于用户集合视图也是一样的,只是多了一个约束的添加
- (void)setUsers:(NSArray *)users
{
NSInteger usersCount = users.count >= UsersCount ? UsersCount : users.count;
for (NSInteger i = 0; i < usersCount; i++) {
UIImageView *imageView = [self createImageViewWithName:[NSString stringWithFormat:@"%ld",(i + 1) * 10]];
[imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(self);
make.left.equalTo(@(i * 25));
make.width.equalTo(@30);
}];
}
_users = users;
}
- (UIImageView *)createImageViewWithName:(NSString *)name
{
UIImageView *imageV = [[UIImageView alloc] init];
imageV.image = [UIImage circleImage:name];
[self addSubview:imageV];
return imageV;
}
这里有一个分类方法 imageV.image = [UIImage circleImage:name],这个是手动写的一个分类,就是做图片切圆的,我们也知道那种Layer的切圆是比较耗费性能的,所以我们很多时候都是用CoreGraphics那一套,当然具体的就不解释了。
一个类方法,一个对象方法
- (instancetype)circleImage
{
UIGraphicsBeginImageContext(self.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
CGContextClip(ctx);
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (instancetype)circleImage:(NSString *)image
{
return [[self imageNamed:image] circleImage];
}
效果呢就是下面👇这样: