// // KBJfPay.m // keyBoard #import "KBJfPay.h" #import "KBJfPayCell.h" #import "FGIAPProductsFilter.h" static NSString * const kKBJfPayCellId = @"kKBJfPayCellId"; @interface KBJfPay () @property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图 // 顶部信息 @property (nonatomic, strong) UILabel *myPointsTitleLabel; // “My Points” @property (nonatomic, strong) UILabel *pointsLabel; // 积分数值 @property (nonatomic, strong) UIImageView *bigCoinImageView; // 右上装饰大金币 pay_big_icon // “Recharge Now” 小标题 @property (nonatomic, strong) UIImageView *smallLeftIcon; // 左侧小金币 shop_jb_icon @property (nonatomic, strong) UILabel *rechargeLabel; // “Recharge Now” // 列表容器(因为需要只有左上/右上圆角) @property (nonatomic, strong) UIView *listContainerView; // 承载 collectionView @property (nonatomic, strong) UICollectionView *collectionView; // 底部按钮/协议 @property (nonatomic, strong) UIButton *payButton; // 充值按钮 @property (nonatomic, strong) UILabel *agreementLabel; // 协议提示 @property (nonatomic, strong) UIButton *agreementButton; // 数据 @property (nonatomic, strong) NSArray *data; // 简单演示数据:@{coins, price} @property (nonatomic, assign) NSInteger selectedIndex; // 当前选中项 @property (nonatomic, strong) FGIAPProductsFilter *filter; @end @implementation KBJfPay - (void)viewDidLoad { [super viewDidLoad]; self.filter = [[FGIAPProductsFilter alloc] init]; self.bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_bg_icon"]]; self.bgImageView.contentMode = UIViewContentModeScaleAspectFill; self.kb_navView.backgroundColor = [UIColor clearColor]; [self.view insertSubview:self.bgImageView belowSubview:self.kb_navView]; [self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; self.kb_titleLabel.text = @"Recharge"; // 默认数据(演示) self.data = @[ @{ @"coins": @690, @"price": @"$6.90",@"product_id" : @"100_coin" }, @{ @"coins": @1280, @"price": @"$12.90" ,@"product_id" : @"vip_a_week" }, @{ @"coins": @3290, @"price": @"$32.90" ,@"product_id" : @"100_coin" }, @{ @"coins": @4990, @"price": @"$49.90" ,@"product_id" : @"100_coin" }, @{ @"coins": @9990, @"price": @"$99.90" ,@"product_id" : @"100_coin" }, @{ @"coins": @19990,@"price": @"$199.90" ,@"product_id" : @"100_coin" }, ]; self.selectedIndex = 1; // 默认选中第二个,贴近截图 // 视图组装 [self.view addSubview:self.myPointsTitleLabel]; [self.view addSubview:self.pointsLabel]; [self.view addSubview:self.listContainerView]; [self.view addSubview:self.bigCoinImageView]; [self.listContainerView addSubview:self.smallLeftIcon]; [self.listContainerView addSubview:self.rechargeLabel]; [self.listContainerView addSubview:self.collectionView]; [self.view addSubview:self.payButton]; [self.view addSubview:self.agreementLabel]; [self.view addSubview:self.agreementButton]; // 布局(mas) [self.myPointsTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view).offset(16); make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT + 40); }]; [self.pointsLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.myPointsTitleLabel); make.top.equalTo(self.myPointsTitleLabel.mas_bottom).offset(4); }]; [self.bigCoinImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(self.view).offset(-12); make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT + 10); make.width.mas_equalTo(131); make.height.mas_equalTo(144); }]; // 列表容器 + 圆角(仅左上/右上) [self.listContainerView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.mas_equalTo(KB_NAV_TOTAL_HEIGHT + 123); make.bottom.equalTo(self.payButton.mas_top).offset(-16); make.height.greaterThanOrEqualTo(@220); }]; [self.smallLeftIcon mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.listContainerView).offset(16); make.top.equalTo(self.listContainerView).offset(16); make.width.height.mas_equalTo(20); }]; [self.rechargeLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(self.smallLeftIcon); make.left.equalTo(self.smallLeftIcon.mas_right).offset(8); }]; [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.left.right.equalTo(self.listContainerView).inset(16); make.top.equalTo(self.smallLeftIcon.mas_bottom).offset(19); }]; [self.agreementButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.view); make.bottom.equalTo(self.view).offset(-KB_SAFE_BOTTOM - 15); }]; [self.agreementLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.view); make.bottom.equalTo(self.agreementButton.mas_top).offset(-8); }]; // 底部按钮 [self.payButton mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view).offset(24); make.right.equalTo(self.view).offset(-24); make.bottom.equalTo(self.agreementLabel.mas_top).offset(-14); make.height.mas_equalTo(58); }]; // 刷新 [self.collectionView reloadData]; // 确保首次进入就出现选中态外边框与阴影 dispatch_async(dispatch_get_main_queue(), ^{ NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:0]; // 让系统层面也处于选中态,便于 setSelected 同步 UI if (ip) { [self.collectionView selectItemAtIndexPath:ip animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } KBJfPayCell *cell = (KBJfPayCell *)[self.collectionView cellForItemAtIndexPath:ip]; if (cell) { [cell applySelected:YES animated:NO]; } }); } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // 再兜底一次(某些布局时机下,首屏 reload 后 cell 还未可见) NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:0]; if (ip) { [self.collectionView selectItemAtIndexPath:ip animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } KBJfPayCell *cell = (KBJfPayCell *)[self.collectionView cellForItemAtIndexPath:ip]; if (cell) { [cell applySelected:YES animated:NO]; } } #pragma mark - UICollectionView Delegate (ensure first show) - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { if (![cell isKindOfClass:KBJfPayCell.class]) { return; } KBJfPayCell *c = (KBJfPayCell *)cell; BOOL sel = (indexPath.item == self.selectedIndex); if (sel) { [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } [c applySelected:sel animated:NO]; } #pragma mark - 圆角蒙版 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; // 仅左上、右上圆角 UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.listContainerView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(20, 20)]; CAShapeLayer *mask = [CAShapeLayer layer]; mask.frame = self.listContainerView.bounds; mask.path = path.CGPath; self.listContainerView.layer.mask = mask; } #pragma mark - UICollectionView - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.data.count; } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { KBJfPayCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBJfPayCellId forIndexPath:indexPath]; NSDictionary *item = self.data[indexPath.item]; NSString *coins = [NSString stringWithFormat:@"%@", item[@"coins"]]; NSString *price = item[@"price"]; // 形如 "$6.90" [cell configCoins:coins price:price]; [cell applySelected:(indexPath.item == self.selectedIndex) animated:NO]; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (self.selectedIndex == indexPath.item) { return; } NSInteger old = self.selectedIndex; self.selectedIndex = indexPath.item; KBJfPayCell *newCell = (KBJfPayCell *)[collectionView cellForItemAtIndexPath:indexPath]; [newCell applySelected:YES animated:YES]; if (old >= 0 && old < self.data.count) { NSIndexPath *oldIP = [NSIndexPath indexPathForItem:old inSection:0]; KBJfPayCell *oldCell = (KBJfPayCell *)[collectionView cellForItemAtIndexPath:oldIP]; [oldCell applySelected:NO animated:YES]; } } // 三列网格 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat totalW = collectionView.bounds.size.width; CGFloat spacing = 10.0; // 列间距 CGFloat columns = 3.0; CGFloat insets = 0; // 已在 mas 中留了左右 16,这里内部 cell 不额外 inset CGFloat w = floor((totalW - insets - spacing * (columns - 1)) / columns); CGFloat h = KBFit(116); return CGSizeMake(MAX(0, w), h); } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 10.0; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 30; } #pragma mark - Actions - (void)onTapPayButton { // 这里只做 UI,实际支付逻辑由调用方接入 // if (self.selectedIndex >= 0 && self.selectedIndex < self.data.count) { // NSDictionary *item = self.data[self.selectedIndex]; // NSString *msg = [NSString stringWithFormat:@"购买:%@ Coins %@", item[@"coins"], item[@"price"]]; // [KBHUD showInfo:msg]; // } NSString *productId = @"com.yolo.vip.1month"; /// 2.获取商品信息 [self.filter requestProductsWith:[NSSet setWithObject:productId] completion:^(NSArray * _Nonnull products) { NSLog(@"====="); /// 3.支付购买 // [[FGIAPManager shared].iap buyProduct:products.firstObject onCompletion:^(NSString * _Nonnull message, FGIAPManagerPurchaseRusult result) { // [self.view makeToast:message]; // }]; }]; } - (void)agreementButtonAction{ [KBHUD showInfo:KBLocalized(@"Open agreement")]; } #pragma mark - Lazy UI - (UILabel *)myPointsTitleLabel { if (!_myPointsTitleLabel) { _myPointsTitleLabel = [UILabel new]; _myPointsTitleLabel.text = @"My Points"; _myPointsTitleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; _myPointsTitleLabel.textColor = [UIColor colorWithHex:KBBlackValue]; } return _myPointsTitleLabel; } - (UILabel *)pointsLabel { if (!_pointsLabel) { _pointsLabel = [UILabel new]; _pointsLabel.text = @"4230"; // 示例值 _pointsLabel.font = [UIFont systemFontOfSize:36 weight:UIFontWeightBold]; _pointsLabel.textColor = [UIColor colorWithHex:KBColorValue]; } return _pointsLabel; } - (UIImageView *)bigCoinImageView { if (!_bigCoinImageView) { _bigCoinImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pay_big_icon"]]; _bigCoinImageView.contentMode = UIViewContentModeScaleAspectFit; } return _bigCoinImageView; } - (UIImageView *)smallLeftIcon { if (!_smallLeftIcon) { _smallLeftIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_jb_icon"]]; _smallLeftIcon.contentMode = UIViewContentModeScaleAspectFit; } return _smallLeftIcon; } - (UILabel *)rechargeLabel { if (!_rechargeLabel) { _rechargeLabel = [UILabel new]; _rechargeLabel.text = @"Recharge Now"; _rechargeLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; _rechargeLabel.textColor = [UIColor colorWithHex:KBBlackValue]; } return _rechargeLabel; } - (UIView *)listContainerView { if (!_listContainerView) { _listContainerView = [UIView new]; // 轻微底色,突出圆角区域(也可用渐变,按需) _listContainerView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.43]; } return _listContainerView; } - (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; [_collectionView registerClass:KBJfPayCell.class forCellWithReuseIdentifier:kKBJfPayCellId]; _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } return _collectionView; } - (UIButton *)payButton { if (!_payButton) { _payButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_payButton setTitle:KBLocalized(@"Recharge Now") forState:UIControlStateNormal]; [_payButton setTitleColor:[UIColor colorWithHex:KBBlackValue] forState:UIControlStateNormal]; _payButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; // 使用现有的切图(若不存在可退化为渐变图片) UIImage *bg = [UIImage imageNamed:@"recharge_now_icon"]; if (bg) { [_payButton setBackgroundImage:bg forState:UIControlStateNormal]; } else { UIImage *fallback = [UIImage kb_gradientImageWithColors:@[[UIColor colorWithHex:0xC7F8F0], [UIColor colorWithHex:0xE8FFF6]] size:CGSizeMake(10, 58) direction:KBGradientDirectionLeftToRight]; [_payButton setBackgroundImage:[fallback resizableImageWithCapInsets:UIEdgeInsetsMake(29, 29, 29, 29) resizingMode:UIImageResizingModeStretch] forState:UIControlStateNormal]; } [_payButton addTarget:self action:@selector(onTapPayButton) forControlEvents:UIControlEventTouchUpInside]; } return _payButton; } - (UILabel *)agreementLabel { if (!_agreementLabel) { _agreementLabel = [UILabel new]; _agreementLabel.text = KBLocalized(@"By clicking Pay, you indicate your agreement to the"); // 简化文案 _agreementLabel.font = [UIFont systemFontOfSize:11 weight:UIFontWeightRegular]; _agreementLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0]; } return _agreementLabel; } - (UIButton *)agreementButton { if (!_agreementButton) { _agreementButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_agreementButton setTitle:KBLocalized(@"《Embership Agreement》") forState:UIControlStateNormal]; [_agreementButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal]; _agreementButton.titleLabel.font = [UIFont systemFontOfSize:10 weight:UIFontWeightSemibold]; [_agreementButton addTarget:self action:@selector(agreementButtonAction) forControlEvents:UIControlEventTouchUpInside]; } return _agreementButton; } @end