diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index a21b4a6..60a8427 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 04122FAA2EC73C0100EF7AB3 /* KBVipPayHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122FA92EC73C0100EF7AB3 /* KBVipPayHeaderView.m */; }; + 04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122FAC2EC73C0100EF7AB3 /* KBVipSubscribeCell.m */; }; + 04122FB02EC73C0100EF7AB3 /* KBVipReviewItemCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122FAF2EC73C0100EF7AB3 /* KBVipReviewItemCell.m */; }; + 04122FB32EC73C0100EF7AB3 /* KBVipReviewListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */; }; 04122F5D2EC5E5A900EF7AB3 /* KBLoginVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122F5B2EC5E5A900EF7AB3 /* KBLoginVM.m */; }; 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122F612EC5F41D00EF7AB3 /* KBUser.m */; }; 04122F6D2EC5F40800EF7AB3 /* NSObject+FGIsNullOrEmpty.m in Sources */ = {isa = PBXBuildFile; fileRef = 04122F6B2EC5F40800EF7AB3 /* NSObject+FGIsNullOrEmpty.m */; }; @@ -189,6 +193,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 04122FA82EC73C0100EF7AB3 /* KBVipPayHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBVipPayHeaderView.h; sourceTree = ""; }; + 04122FA92EC73C0100EF7AB3 /* KBVipPayHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVipPayHeaderView.m; sourceTree = ""; }; + 04122FAB2EC73C0100EF7AB3 /* KBVipSubscribeCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBVipSubscribeCell.h; sourceTree = ""; }; + 04122FAC2EC73C0100EF7AB3 /* KBVipSubscribeCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVipSubscribeCell.m; sourceTree = ""; }; + 04122FAE2EC73C0100EF7AB3 /* KBVipReviewItemCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBVipReviewItemCell.h; sourceTree = ""; }; + 04122FAF2EC73C0100EF7AB3 /* KBVipReviewItemCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVipReviewItemCell.m; sourceTree = ""; }; + 04122FB12EC73C0100EF7AB3 /* KBVipReviewListCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBVipReviewListCell.h; sourceTree = ""; }; + 04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVipReviewListCell.m; sourceTree = ""; }; 04122F592EC5D40000EF7AB3 /* KBAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAPI.h; sourceTree = ""; }; 04122F5A2EC5E5A900EF7AB3 /* KBLoginVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBLoginVM.h; sourceTree = ""; }; 04122F5B2EC5E5A900EF7AB3 /* KBLoginVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBLoginVM.m; sourceTree = ""; }; @@ -552,6 +564,14 @@ children = ( 04122F7B2EC5FC5500EF7AB3 /* KBJfPayCell.h */, 04122F7C2EC5FC5500EF7AB3 /* KBJfPayCell.m */, + 04122FA82EC73C0100EF7AB3 /* KBVipPayHeaderView.h */, + 04122FA92EC73C0100EF7AB3 /* KBVipPayHeaderView.m */, + 04122FAB2EC73C0100EF7AB3 /* KBVipSubscribeCell.h */, + 04122FAC2EC73C0100EF7AB3 /* KBVipSubscribeCell.m */, + 04122FAE2EC73C0100EF7AB3 /* KBVipReviewItemCell.h */, + 04122FAF2EC73C0100EF7AB3 /* KBVipReviewItemCell.m */, + 04122FB12EC73C0100EF7AB3 /* KBVipReviewListCell.h */, + 04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */, ); path = V; sourceTree = ""; @@ -1591,6 +1611,7 @@ 04122F882EC6F07F00EF7AB3 /* KBFullAccessManager.m in Sources */, 04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */, 04122F8B2EC6F7C800EF7AB3 /* IAPVerifyTransactionObj.m in Sources */, + 04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */, 049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */, 048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */, 048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */, @@ -1656,12 +1677,14 @@ 04122F6E2EC5F40800EF7AB3 /* FGIAPProductsFilter.m in Sources */, 04122F6F2EC5F40800EF7AB3 /* FGIAPManager.m in Sources */, 04122F702EC5F40800EF7AB3 /* FGIAPService.m in Sources */, + 04122FB32EC73C0100EF7AB3 /* KBVipReviewListCell.m in Sources */, 048908DA2EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout.m in Sources */, 04C6EABF2EAF86530089C901 /* main.m in Sources */, 04FC95CC2EB1E780007BD342 /* BaseTabBarController.m in Sources */, 047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */, 0477BE042EBC83130055D639 /* HomeMainVC.m in Sources */, 0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */, + 04122FAA2EC73C0100EF7AB3 /* KBVipPayHeaderView.m in Sources */, 049FB2432EC4BBB700FAB05D /* KBLoginPopView.m in Sources */, 048908CC2EBE373500FABA60 /* KBSearchBarView.m in Sources */, 04122F872EC6198C00EF7AB3 /* WMDragView.m in Sources */, @@ -1679,6 +1702,7 @@ A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */, 047C65532EBCBAC60035E841 /* KBCommunityVC.m in Sources */, A1B2C4212EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */, + 04122FB02EC73C0100EF7AB3 /* KBVipReviewItemCell.m in Sources */, 04122F822EC5FC6F00EF7AB3 /* KBJfPay.m in Sources */, 04122F5D2EC5E5A900EF7AB3 /* KBLoginVM.m in Sources */, 0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */, diff --git a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/Contents.json b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/Contents.json index bcd7482..47ff9d0 100644 --- a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/Contents.json +++ b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "home_left_bg 1.png", + "filename" : "home_left_bg.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "home_left_bg 2.png", + "filename" : "home_left_bg 1.png", "idiom" : "universal", "scale" : "3x" } diff --git a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 1.png b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 1.png index 9d3ddc8..aa01ad4 100644 Binary files a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 1.png and b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 1.png differ diff --git a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 2.png b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 2.png deleted file mode 100644 index 9d3ddc8..0000000 Binary files a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg 2.png and /dev/null differ diff --git a/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg.png b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg.png new file mode 100644 index 0000000..aa01ad4 Binary files /dev/null and b/keyBoard/Assets.xcassets/Home/home_left_bg.imageset/home_left_bg.png differ diff --git a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/Contents.json b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/Contents.json index e64eb3f..1fee6fb 100644 --- a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/Contents.json +++ b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "home_right_bg 1.png", + "filename" : "home_right_bg.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "home_right_bg 2.png", + "filename" : "home_right_bg 1.png", "idiom" : "universal", "scale" : "3x" } diff --git a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 1.png b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 1.png index 4972b28..a785752 100644 Binary files a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 1.png and b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 1.png differ diff --git a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 2.png b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 2.png deleted file mode 100644 index 4972b28..0000000 Binary files a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg 2.png and /dev/null differ diff --git a/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg.png b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg.png new file mode 100644 index 0000000..a785752 Binary files /dev/null and b/keyBoard/Assets.xcassets/Home/home_right_bg.imageset/home_right_bg.png differ diff --git a/keyBoard/Class/Home/V/HomeHeadView.m b/keyBoard/Class/Home/V/HomeHeadView.m index 793e4d9..0d40fee 100644 --- a/keyBoard/Class/Home/V/HomeHeadView.m +++ b/keyBoard/Class/Home/V/HomeHeadView.m @@ -8,7 +8,7 @@ #import "HomeHeadView.h" #import "UIImage+KBColor.h" #import "KBTopImageButton.h" -#import "KBJfPay.h" +#import "KBVipPay.h" @interface HomeHeadView() @@ -163,7 +163,7 @@ #pragma mark - Actions - (void)onTapBuyAction { // if (self.onTapBuy) { self.onTapBuy(); } - KBJfPay *vc = [[KBJfPay alloc] init]; + KBVipPay *vc = [[KBVipPay alloc] init]; [KB_CURRENT_NAV pushViewController:vc animated:true]; } diff --git a/keyBoard/Class/Home/V/KBPanModalView.m b/keyBoard/Class/Home/V/KBPanModalView.m index cd4e2cb..b24f48c 100644 --- a/keyBoard/Class/Home/V/KBPanModalView.m +++ b/keyBoard/Class/Home/V/KBPanModalView.m @@ -180,11 +180,11 @@ [self.leftBgImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.top.equalTo(self.secWhiteContentView); -// make.bottom.equalTo(self.secWhiteContentView); + make.height.mas_equalTo(466); }]; [self.rightBgImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.top.equalTo(self.secWhiteContentView); -// make.bottom.equalTo(self.secWhiteContentView); + make.height.mas_equalTo(466); }]; diff --git a/keyBoard/Class/Pay/V/KBVipPayHeaderView.h b/keyBoard/Class/Pay/V/KBVipPayHeaderView.h new file mode 100644 index 0000000..4312663 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipPayHeaderView.h @@ -0,0 +1,19 @@ +// +// KBVipPayHeaderView.h +// keyBoard +// +// 顶部大头部视图:包含 5 张图片 +// - 高度:484 + 导航总高度 +// - 图片资源:pay_vip_icon、pay_ai_icon、pay_keyboard_icon、pay_chat_icon、pay_emotion_icon +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBVipPayHeaderView : UICollectionReusableView + + +NS_ASSUME_NONNULL_END +@end + diff --git a/keyBoard/Class/Pay/V/KBVipPayHeaderView.m b/keyBoard/Class/Pay/V/KBVipPayHeaderView.m new file mode 100644 index 0000000..7160bb2 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipPayHeaderView.m @@ -0,0 +1,147 @@ +// +// KBVipPayHeaderView.m +// keyBoard +// +// 中文注释:顶部头图区域,使用 Masonry 布局,包含 5 张图片。 +// + +#import "KBVipPayHeaderView.h" + +@interface KBVipPayHeaderView () +// 容器(为了便于以 KB_NAV_TOTAL_HEIGHT 作为内容起点) +@property (nonatomic, strong) UIView *containerView; +// 顶部大图:pay_vip_icon +@property (nonatomic, strong) UIImageView *vipImageView; +// 功能图标四宫格 +@property (nonatomic, strong) UIImageView *aiImageView; +@property (nonatomic, strong) UIImageView *keyboardImageView; +@property (nonatomic, strong) UIImageView *chatImageView; +@property (nonatomic, strong) UIImageView *emotionImageView; +@end + +@implementation KBVipPayHeaderView + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.backgroundColor = [UIColor clearColor]; + + [self addSubview:self.containerView]; + [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self); + }]; + + // 1. 顶部大图(略微下移,避开自定义导航栏) + [self.containerView addSubview:self.vipImageView]; + [self.vipImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.containerView).offset(16); + make.right.equalTo(self.containerView).offset(-16); + make.top.equalTo(self.containerView).offset(KB_NAV_TOTAL_HEIGHT + 8); + make.height.mas_equalTo(KBFit(180)); + }]; + + // 2. 下方四宫格图标(简单示意) + UIView *g1 = [self gridItemWithImageView:self.aiImageView]; + UIView *g2 = [self gridItemWithImageView:self.keyboardImageView]; + UIView *g3 = [self gridItemWithImageView:self.chatImageView]; + UIView *g4 = [self gridItemWithImageView:self.emotionImageView]; + [self.containerView addSubview:g1]; + [self.containerView addSubview:g2]; + [self.containerView addSubview:g3]; + [self.containerView addSubview:g4]; + + CGFloat spacing = 12; + [g1 mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.vipImageView); + make.top.equalTo(self.vipImageView.mas_bottom).offset(18); + make.height.mas_equalTo(KBFit(90)); + }]; + [g2 mas_makeConstraints:^(MASConstraintMaker *make) { + make.right.equalTo(self.vipImageView); + make.top.equalTo(g1); + make.height.equalTo(g1); + make.left.equalTo(g1.mas_right).offset(spacing); + make.width.equalTo(g1); + }]; + [g3 mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(g1); + make.top.equalTo(g1.mas_bottom).offset(spacing); + make.height.equalTo(g1); + make.width.equalTo(g1); + }]; + [g4 mas_makeConstraints:^(MASConstraintMaker *make) { + make.right.equalTo(self.vipImageView); + make.top.equalTo(g3); + make.left.equalTo(g3.mas_right).offset(spacing); + make.height.equalTo(g1); + make.width.equalTo(g1); + make.bottom.lessThanOrEqualTo(self.containerView).offset(-12); + }]; + } + return self; +} + +#pragma mark - Helpers +- (UIView *)gridItemWithImageView:(UIImageView *)iv { + // 简单白底圆角卡片承载图标 + UIView *v = [UIView new]; + v.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0]; + v.layer.cornerRadius = 12; + v.layer.masksToBounds = YES; + [v addSubview:iv]; + [iv mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.equalTo(v); + make.width.height.mas_equalTo(40); + }]; + return v; +} + +#pragma mark - Lazy +- (UIView *)containerView { + if (!_containerView) { + _containerView = [UIView new]; + _containerView.backgroundColor = [UIColor clearColor]; + } + return _containerView; +} + +- (UIImageView *)vipImageView { + if (!_vipImageView) { + _vipImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_vip_icon"]]; + _vipImageView.contentMode = UIViewContentModeScaleAspectFill; + _vipImageView.clipsToBounds = YES; + _vipImageView.layer.cornerRadius = 14; + } + return _vipImageView; +} + +- (UIImageView *)aiImageView { + if (!_aiImageView) { + _aiImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_ai_icon"]]; + _aiImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _aiImageView; +} +- (UIImageView *)keyboardImageView { + if (!_keyboardImageView) { + _keyboardImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_keyboard_icon"]]; + _keyboardImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _keyboardImageView; +} +- (UIImageView *)chatImageView { + if (!_chatImageView) { + _chatImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_chat_icon"]]; + _chatImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _chatImageView; +} +- (UIImageView *)emotionImageView { + if (!_emotionImageView) { + _emotionImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_emotion_icon"]]; + _emotionImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _emotionImageView; +} + +@end + diff --git a/keyBoard/Class/Pay/V/KBVipReviewItemCell.h b/keyBoard/Class/Pay/V/KBVipReviewItemCell.h new file mode 100644 index 0000000..cbdd238 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipReviewItemCell.h @@ -0,0 +1,17 @@ +// +// KBVipReviewItemCell.h +// keyBoard +// +// 横向列表中的 item,包含 5 个爱心图标:pay_5aixin_icon +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBVipReviewItemCell : UICollectionViewCell +- (void)configWithName:(NSString *)name text:(NSString *)text; +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Pay/V/KBVipReviewItemCell.m b/keyBoard/Class/Pay/V/KBVipReviewItemCell.m new file mode 100644 index 0000000..aca2622 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipReviewItemCell.m @@ -0,0 +1,124 @@ +// +// KBVipReviewItemCell.m +// keyBoard +// +// 中文注释:简单的好评卡片,顶部头像/昵称,下面 5 个爱心图标,再下一行一段文案。 +// + +#import "KBVipReviewItemCell.h" + +@interface KBVipReviewItemCell () +@property (nonatomic, strong) UIView *cardView; +@property (nonatomic, strong) UIImageView *avatarView; +@property (nonatomic, strong) UILabel *nameLabel; +@property (nonatomic, strong) UIStackView *heartsStack; +@property (nonatomic, strong) UILabel *contentLabel; +@end + +@implementation KBVipReviewItemCell + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.contentView.backgroundColor = UIColor.clearColor; + + [self.contentView addSubview:self.cardView]; + [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.contentView); + }]; + + [self.cardView addSubview:self.avatarView]; + [self.cardView addSubview:self.nameLabel]; + [self.cardView addSubview:self.heartsStack]; + [self.cardView addSubview:self.contentLabel]; + + [self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.top.equalTo(self.cardView).offset(12); + make.width.height.mas_equalTo(28); + }]; + [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.avatarView); + make.left.equalTo(self.avatarView.mas_right).offset(8); + make.right.lessThanOrEqualTo(self.cardView).offset(-12); + }]; + [self.heartsStack mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.avatarView); + make.top.equalTo(self.avatarView.mas_bottom).offset(8); + }]; + [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.avatarView); + make.right.equalTo(self.cardView).offset(-12); + make.top.equalTo(self.heartsStack.mas_bottom).offset(6); + make.bottom.lessThanOrEqualTo(self.cardView).offset(-12); + }]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + self.cardView.layer.cornerRadius = 12; + self.cardView.layer.masksToBounds = YES; +} + +- (void)configWithName:(NSString *)name text:(NSString *)text { + self.nameLabel.text = name.length ? name : @"User"; + self.contentLabel.text = text.length ? text : @"I highly recommend this app."; +} + +#pragma mark - Lazy +- (UIView *)cardView { + if (!_cardView) { + _cardView = [UIView new]; + _cardView.backgroundColor = [UIColor whiteColor]; + } + return _cardView; +} +- (UIImageView *)avatarView { + if (!_avatarView) { + _avatarView = [UIImageView new]; + _avatarView.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0]; + _avatarView.layer.cornerRadius = 14; + _avatarView.layer.masksToBounds = YES; + } + return _avatarView; +} +- (UILabel *)nameLabel { + if (!_nameLabel) { + _nameLabel = [UILabel new]; + _nameLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _nameLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; + _nameLabel.text = @"Sdsd666"; + } + return _nameLabel; +} +- (UIStackView *)heartsStack { + if (!_heartsStack) { + _heartsStack = [[UIStackView alloc] init]; + _heartsStack.axis = UILayoutConstraintAxisHorizontal; + _heartsStack.spacing = 4; + _heartsStack.alignment = UIStackViewAlignmentCenter; + _heartsStack.distribution = UIStackViewDistributionFillProportionally; + for (int i = 0; i < 5; i++) { + UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_5aixin_icon"]]; + iv.contentMode = UIViewContentModeScaleAspectFit; + [iv mas_makeConstraints:^(MASConstraintMaker *make) { + make.width.height.mas_equalTo(16); + }]; + [_heartsStack addArrangedSubview:iv]; + } + } + return _heartsStack; +} +- (UILabel *)contentLabel { + if (!_contentLabel) { + _contentLabel = [UILabel new]; + _contentLabel.textColor = [UIColor colorWithWhite:0.25 alpha:1.0]; + _contentLabel.font = [UIFont systemFontOfSize:12]; + _contentLabel.numberOfLines = 2; + _contentLabel.text = @"I Highly Recommend This App. It Taught Me How To Chat"; + } + return _contentLabel; +} + +@end + diff --git a/keyBoard/Class/Pay/V/KBVipReviewListCell.h b/keyBoard/Class/Pay/V/KBVipReviewListCell.h new file mode 100644 index 0000000..b3f96ae --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipReviewListCell.h @@ -0,0 +1,16 @@ +// +// KBVipReviewListCell.h +// keyBoard +// +// 末尾的横向滚动列表 Cell,内部自带一个 UICollectionView。 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBVipReviewListCell : UICollectionViewCell +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Pay/V/KBVipReviewListCell.m b/keyBoard/Class/Pay/V/KBVipReviewListCell.m new file mode 100644 index 0000000..c1cadd1 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipReviewListCell.m @@ -0,0 +1,82 @@ +// +// KBVipReviewListCell.m +// keyBoard +// +// 中文注释:底部 Cell,内部是横向滚动的 UICollectionView。 +// + +#import "KBVipReviewListCell.h" +#import "KBVipReviewItemCell.h" + +static NSString * const kKBVipReviewItemCellId = @"kKBVipReviewItemCellId"; + +@interface KBVipReviewListCell () +@property (nonatomic, strong) UICollectionView *collectionView; // 内部横向滚动 +@property (nonatomic, strong) NSArray *data; // 简单模拟数据 +@end + +@implementation KBVipReviewListCell + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.contentView.backgroundColor = [UIColor clearColor]; + [self.contentView addSubview:self.collectionView]; + [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.contentView).insets(UIEdgeInsetsMake(0, 0, 0, 0)); + }]; + + // 模拟数据 + _data = @[ + @{@"name":@"Sdsd666", @"text":@"I Highly Recommend This App. It Taught Me How To Chat"}, + @{@"name":@"Joyce", @"text":@"Great keyboard and AI features!"}, + @{@"name":@"Luna", @"text":@"Amazing app, love it."}, + @{@"name":@"Mark", @"text":@"Helps with chat and emotion."}, + @{@"name":@"Alan", @"text":@"Useful personalized keyboard."}, + @{@"name":@"Coco", @"text":@"Recommend to friends."}, + ]; + } + return self; +} + +#pragma mark - DataSource +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return self.data.count; +} +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + KBVipReviewItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipReviewItemCellId forIndexPath:indexPath]; + NSDictionary *d = self.data[indexPath.item]; + [cell configWithName:d[@"name"] text:d[@"text"]]; + return cell; +} + +#pragma mark - FlowLayout +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + CGFloat w = floor((KB_SCREEN_WIDTH - 16 - 16 - 10) / 2.0); // 一屏展示两个 + return CGSizeMake(MAX(120, w), 120); +} +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 10; } +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 10; } +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsMake(0, 16, 0, 16); +} + +#pragma mark - Lazy +- (UICollectionView *)collectionView { + if (!_collectionView) { + UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.backgroundColor = UIColor.clearColor; + _collectionView.showsHorizontalScrollIndicator = NO; + _collectionView.dataSource = self; + _collectionView.delegate = self; + [_collectionView registerClass:KBVipReviewItemCell.class forCellWithReuseIdentifier:kKBVipReviewItemCellId]; + if (@available(iOS 11.0, *)) { + _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } + } + return _collectionView; +} + +@end + diff --git a/keyBoard/Class/Pay/V/KBVipSubscribeCell.h b/keyBoard/Class/Pay/V/KBVipSubscribeCell.h new file mode 100644 index 0000000..defa7c8 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipSubscribeCell.h @@ -0,0 +1,20 @@ +// +// KBVipSubscribeCell.h +// keyBoard +// +// 订阅选项 Cell:右侧选择按钮(pay_circle_normal / pay_circle_sel) +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBVipSubscribeCell : UICollectionViewCell +/// 配置展示文案 +- (void)configTitle:(NSString *)title price:(NSString *)price strike:(nullable NSString *)strike; +/// 同步选中态(更新右侧选择图标、边框颜色) +- (void)applySelected:(BOOL)selected animated:(BOOL)animated; +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Pay/V/KBVipSubscribeCell.m b/keyBoard/Class/Pay/V/KBVipSubscribeCell.m new file mode 100644 index 0000000..a2b1304 --- /dev/null +++ b/keyBoard/Class/Pay/V/KBVipSubscribeCell.m @@ -0,0 +1,162 @@ +// +// KBVipSubscribeCell.m +// keyBoard +// +// 中文注释:订阅选项样式,右侧有选中图标,使用 mas 布局 + 懒加载。 +// + +#import "KBVipSubscribeCell.h" + +@interface KBVipSubscribeCell () +@property (nonatomic, strong) UIView *cardView; // 白色卡片背景 +@property (nonatomic, strong) UILabel *titleLabel; // “Monthly Subscription” +@property (nonatomic, strong) UILabel *priceLabel; // “$4.49” +@property (nonatomic, strong) UILabel *strikeLabel; // 删除线原价 +@property (nonatomic, strong) UIButton *selectButton; // 右侧选择按钮 +@property (nonatomic, strong) CAShapeLayer *borderLayer; // 选中边框 +@end + +@implementation KBVipSubscribeCell + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.contentView.backgroundColor = [UIColor clearColor]; + + [self.contentView addSubview:self.cardView]; + [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.contentView); + }]; + + [self.cardView addSubview:self.titleLabel]; + [self.cardView addSubview:self.priceLabel]; + [self.cardView addSubview:self.strikeLabel]; + [self.cardView addSubview:self.selectButton]; + + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.cardView).offset(16); + make.top.equalTo(self.cardView).offset(16); + }]; + [self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.titleLabel); + make.top.equalTo(self.titleLabel.mas_bottom).offset(6); + }]; + [self.strikeLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.priceLabel.mas_right).offset(10); + make.centerY.equalTo(self.priceLabel); + }]; + [self.selectButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.cardView); + make.right.equalTo(self.cardView).offset(-16); + make.width.height.mas_equalTo(28); + }]; + + // 边框层(选中时显示主题绿) + self.borderLayer = [CAShapeLayer layer]; + self.borderLayer.strokeColor = [UIColor colorWithWhite:0.9 alpha:1.0].CGColor; + // 使用透明填充,避免遮挡内部子视图;只显示描边 + self.borderLayer.fillColor = UIColor.clearColor.CGColor; + self.borderLayer.lineWidth = 1.5; + // 放到底层,避免盖住 label/button(修复滚动后偶现内容被遮挡变空白) + [self.cardView.layer insertSublayer:self.borderLayer atIndex:0]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + CGFloat radius = 16; + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.cardView.bounds cornerRadius:radius]; + self.cardView.layer.cornerRadius = radius; + self.cardView.layer.masksToBounds = YES; + self.borderLayer.frame = self.cardView.bounds; + self.borderLayer.path = path.CGPath; +} + +- (void)prepareForReuse { + [super prepareForReuse]; + [self applySelected:NO animated:NO]; +} + +- (void)setSelected:(BOOL)selected { + [super setSelected:selected]; + [self applySelected:selected animated:NO]; +} + +- (void)configTitle:(NSString *)title price:(NSString *)price strike:(nullable NSString *)strike { + self.titleLabel.text = title.length ? title : @"Monthly Subscription"; + self.priceLabel.text = price.length ? price : @"$4.49"; + self.strikeLabel.hidden = (strike.length == 0); + if (strike.length) { + // 加删除线 + NSDictionary *attr = @{ + NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle), + NSForegroundColorAttributeName: [UIColor colorWithWhite:0.7 alpha:1.0] + }; + self.strikeLabel.attributedText = [[NSAttributedString alloc] initWithString:strike attributes:attr]; + } +} + +- (void)applySelected:(BOOL)selected animated:(BOOL)animated { + UIImage *img = [UIImage imageNamed:(selected ? @"pay_circle_sel" : @"pay_circle_normal")]; + [self.selectButton setImage:img forState:UIControlStateNormal]; + CGColorRef color = (selected ? [UIColor colorWithHex:KBColorValue].CGColor : [UIColor colorWithWhite:0.9 alpha:1.0].CGColor); + void (^changes)(void) = ^{ + self.borderLayer.strokeColor = color; + self.cardView.layer.shadowOpacity = selected ? 0.12 : 0.0; + self.cardView.layer.shadowColor = [UIColor colorWithHex:KBColorValue].CGColor; + self.cardView.layer.shadowRadius = selected ? 8 : 0; + self.cardView.layer.shadowOffset = CGSizeMake(0, selected ? 4 : 0); + }; + if (animated) { + [UIView animateWithDuration:0.15 animations:changes]; + } else { + changes(); + } +} + +#pragma mark - Lazy +- (UIView *)cardView { + if (!_cardView) { + _cardView = [UIView new]; + _cardView.backgroundColor = [UIColor whiteColor]; + } + return _cardView; +} +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.text = @"Monthly Subscription"; + _titleLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; + } + return _titleLabel; +} +- (UILabel *)priceLabel { + if (!_priceLabel) { + _priceLabel = [UILabel new]; + _priceLabel.text = @"$4.49"; + _priceLabel.textColor = [UIColor colorWithHex:KBBlackValue]; + _priceLabel.font = [UIFont systemFontOfSize:28 weight:UIFontWeightBold]; + } + return _priceLabel; +} +- (UILabel *)strikeLabel { + if (!_strikeLabel) { + _strikeLabel = [UILabel new]; + _strikeLabel.text = @"$4.49"; + _strikeLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; + _strikeLabel.textColor = [UIColor colorWithWhite:0.7 alpha:1.0]; + } + return _strikeLabel; +} +- (UIButton *)selectButton { + if (!_selectButton) { + _selectButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_selectButton setImage:[UIImage imageNamed:@"pay_circle_normal"] forState:UIControlStateNormal]; + _selectButton.userInteractionEnabled = NO; // 由外层控制选中 + _selectButton.contentMode = UIViewContentModeCenter; + } + return _selectButton; +} + +@end diff --git a/keyBoard/Class/Pay/VC/KBVipPay.h b/keyBoard/Class/Pay/VC/KBVipPay.h index 7c03641..d5a04d2 100644 --- a/keyBoard/Class/Pay/VC/KBVipPay.h +++ b/keyBoard/Class/Pay/VC/KBVipPay.h @@ -9,8 +9,8 @@ NS_ASSUME_NONNULL_BEGIN +/// VIP 订阅页(整体使用 UICollectionView,上下滚动) @interface KBVipPay : UIViewController - @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Pay/VC/KBVipPay.m b/keyBoard/Class/Pay/VC/KBVipPay.m index 8a4694f..8e267f8 100644 --- a/keyBoard/Class/Pay/VC/KBVipPay.m +++ b/keyBoard/Class/Pay/VC/KBVipPay.m @@ -6,8 +6,19 @@ // #import "KBVipPay.h" +#import "KBVipPayHeaderView.h" +#import "KBVipSubscribeCell.h" +#import "KBVipReviewListCell.h" -@interface KBVipPay () +static NSString * const kKBVipHeaderId = @"kKBVipHeaderId"; +static NSString * const kKBVipSubscribeCellId = @"kKBVipSubscribeCellId"; +static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId"; + +@interface KBVipPay () +@property (nonatomic, strong) UICollectionView *collectionView; // 主列表(竖向滚动) +@property (nonatomic, strong) NSArray *plans; // 订阅方案数组 +@property (nonatomic, assign) NSInteger selectedIndex; // 当前选中的方案索引 +@property (nonatomic, strong) UIButton *closeButton; // 当前选中的方案索引 @end @@ -15,17 +26,168 @@ - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view. + // 标题与导航样式 +// self.kb_titleLabel.text = @"VIP"; +// self.kb_navView.backgroundColor = [UIColor clearColor]; + + self.view.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0]; + + // 初始化数据(简单演示) + self.plans = @[ + @{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"}, + @{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"}, + @{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"}, + ]; + self.selectedIndex = 1; // 默认选中第二项 + [self.view addSubview:self.closeButton]; + [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT); + make.left.equalTo(self.view).offset(15); + make.width.height.mas_equalTo(36); + }]; + // 组装主列表 + [self.view addSubview:self.collectionView]; + [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.bottom.equalTo(self.view); + make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT); + }]; + + [self.collectionView reloadData]; } -/* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + // 首次进入,确保订阅项保持选中态(避免首屏仅显示 Header,待滚动出现时没有选中样式) + NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:1]; + if (!ip) { return; } + // 系统层面也置为选中 + [self.collectionView selectItemAtIndexPath:ip animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + // 若此时 cell 不可见,willDisplay 再兜底 + KBVipSubscribeCell *cell = (KBVipSubscribeCell *)[self.collectionView cellForItemAtIndexPath:ip]; + if (cell) { [cell applySelected:YES animated:NO]; } } -*/ +#pragma mark - UICollectionView DataSource + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + // 0:头部;1:订阅选项;2:底部横滑好评 + return 3; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + if (section == 1) { return self.plans.count; } + if (section == 2) { return 1; } + return 0; // 头部仅使用 header +} + +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 1) { + KBVipSubscribeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipSubscribeCellId forIndexPath:indexPath]; + NSDictionary *plan = self.plans[indexPath.item]; + [cell configTitle:plan[@"title"] price:plan[@"price"] strike:plan[@"strike"]]; + [cell applySelected:(indexPath.item == self.selectedIndex) animated:NO]; + return cell; + } else { + KBVipReviewListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipReviewListCellId forIndexPath:indexPath]; + return cell; + } +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0 && [kind isEqualToString:UICollectionElementKindSectionHeader]) { + KBVipPayHeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kKBVipHeaderId forIndexPath:indexPath]; + return header; + } + return [UICollectionReusableView new]; +} + +#pragma mark - UICollectionView Delegate +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section != 1) { return; } + if (self.selectedIndex == indexPath.item) { return; } + NSInteger old = self.selectedIndex; + self.selectedIndex = indexPath.item; + + KBVipSubscribeCell *newCell = (KBVipSubscribeCell *)[collectionView cellForItemAtIndexPath:indexPath]; + [newCell applySelected:YES animated:YES]; + if (old >= 0 && old < self.plans.count) { + NSIndexPath *oldIP = [NSIndexPath indexPathForItem:old inSection:1]; + KBVipSubscribeCell *oldCell = (KBVipSubscribeCell *)[collectionView cellForItemAtIndexPath:oldIP]; + [oldCell applySelected:NO animated:YES]; + } +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + // 兜底:当订阅项第一次出现在屏幕上,强制同步选中样式 + if (indexPath.section == 1 && [cell isKindOfClass:KBVipSubscribeCell.class]) { + BOOL sel = (indexPath.item == self.selectedIndex); + KBVipSubscribeCell *c = (KBVipSubscribeCell *)cell; + if (sel) { + [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + } + [c applySelected:sel animated:NO]; + } +} + +#pragma mark - FlowLayout +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + CGFloat w = KB_SCREEN_WIDTH - 32; + if (indexPath.section == 1) { + return CGSizeMake(w, 106); + } else { + return CGSizeMake(w, 140); + } +} + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { + if (section == 0) { + // 头部高度 = 484 + 导航栏高度 + return CGSizeMake(KB_SCREEN_WIDTH, 484 + KB_NAV_TOTAL_HEIGHT); + } + return CGSizeZero; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + if (section == 1) { return 14; } + return 0; +} + +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + if (section == 1) { + return UIEdgeInsetsMake(0, 16, 10, 16); + } else if (section == 2) { + return UIEdgeInsetsMake(10, 16, 20, 16); + } + return UIEdgeInsetsZero; +} + +#pragma mark - Lazy +- (UICollectionView *)collectionView { + if (!_collectionView) { + UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.backgroundColor = [UIColor clearColor]; + _collectionView.dataSource = self; + _collectionView.delegate = self; + _collectionView.alwaysBounceVertical = YES; + if (@available(iOS 11.0, *)) { + _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } + [_collectionView registerClass:KBVipPayHeaderView.class forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kKBVipHeaderId]; + [_collectionView registerClass:KBVipSubscribeCell.class forCellWithReuseIdentifier:kKBVipSubscribeCellId]; + [_collectionView registerClass:KBVipReviewListCell.class forCellWithReuseIdentifier:kKBVipReviewListCellId]; + } + return _collectionView; +} + + +- (UIButton *)closeButton { + if (!_closeButton) { + _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_closeButton setImage:[UIImage imageNamed:@"white_close_icon"] forState:UIControlStateNormal]; + [_closeButton addTarget:self action:@selector(onTapClose) forControlEvents:UIControlEventTouchUpInside]; + } + return _closeButton; +} @end