Files
keyboard/keyBoard/Class/Pay/VC/KBJfPay.m
2025-11-14 16:34:01 +08:00

372 lines
16 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBJfPay.m
// keyBoard
#import "KBJfPay.h"
#import "KBJfPayCell.h"
#import "FGIAPProductsFilter.h"
static NSString * const kKBJfPayCellId = @"kKBJfPayCellId";
@interface KBJfPay () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@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<NSDictionary *> *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<SKProduct *> * _Nonnull products) {
NSLog(@"=====");
/// 3.支付购买
// [[FGIAPManager shared].iap buyProduct:products.firstObject onCompletion:^(NSString * _Nonnull message, FGIAPManagerPurchaseRusult result) {
// [self.view makeToast:message];
// }];
}];
}
- (void)agreementButtonAction{
[KBHUD showInfo:@"跳转协议"];
}
#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:@"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 = @"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:@"《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