2
This commit is contained in:
@@ -9,6 +9,11 @@
|
||||
#import "KBVipPayHeaderView.h"
|
||||
#import "KBVipSubscribeCell.h"
|
||||
#import "KBVipReviewListCell.h"
|
||||
#import "PayVM.h"
|
||||
#import "KBPayProductModel.h"
|
||||
#import "FGIAPProductsFilter.h"
|
||||
#import "FGIAPManager.h"
|
||||
#import "KBBizCode.h"
|
||||
|
||||
static NSString * const kKBVipHeaderId = @"kKBVipHeaderId";
|
||||
static NSString * const kKBVipSubscribeCellId = @"kKBVipSubscribeCellId";
|
||||
@@ -16,7 +21,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
|
||||
@interface KBVipPay () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
@property (nonatomic, strong) UICollectionView *collectionView; // 主列表(竖向滚动)
|
||||
@property (nonatomic, strong) NSArray<NSDictionary *> *plans; // 订阅方案数组
|
||||
@property (nonatomic, strong) NSArray<KBPayProductModel *> *plans; // 订阅方案数组
|
||||
@property (nonatomic, assign) NSInteger selectedIndex; // 当前选中的方案索引
|
||||
@property (nonatomic, strong) UIButton *closeButton; // 当前选中的方案索引
|
||||
@property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图
|
||||
@@ -27,6 +32,8 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
@property (nonatomic, strong) UIButton *payButton; // 支付按钮(背景图)
|
||||
@property (nonatomic, strong) UILabel *agreementLabel; // 协议提示
|
||||
@property (nonatomic, strong) UIButton *agreementButton; // 《Embership Agreement》
|
||||
@property (nonatomic, strong) PayVM *payVM;
|
||||
@property (nonatomic, strong) FGIAPProductsFilter *filter;
|
||||
|
||||
@end
|
||||
|
||||
@@ -46,13 +53,10 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
make.left.top.right.equalTo(self.view);
|
||||
make.height.mas_equalTo(224);
|
||||
}];
|
||||
// 初始化数据(简单演示)
|
||||
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.payVM = [PayVM new];
|
||||
self.filter = [[FGIAPProductsFilter alloc] init];
|
||||
self.plans = @[];
|
||||
self.selectedIndex = NSNotFound;
|
||||
|
||||
// 组装主列表
|
||||
[self.view addSubview:self.collectionView];
|
||||
@@ -89,18 +93,67 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
// 预计算 Header 高度(由内部约束决定)
|
||||
self.headerHeight = [self kb_calcHeaderHeightForWidth:KB_SCREEN_WIDTH];
|
||||
[self.collectionView reloadData];
|
||||
|
||||
[self fetchSubscriptionPlans];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
// 首次进入,确保订阅项保持选中态(避免首屏仅显示 Header,待滚动出现时没有选中样式)
|
||||
[self selectCurrentPlanAnimated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Data
|
||||
- (void)fetchSubscriptionPlans {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.payVM fetchSubscriptionProductsNeedShow:YES completion:^(NSInteger sta, NSString * _Nullable msg, NSArray<KBPayProductModel *> * _Nullable products) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self) { return; }
|
||||
if (sta != KBBizCodeSuccess || ![products isKindOfClass:NSArray.class]) {
|
||||
self.plans = @[];
|
||||
self.selectedIndex = NSNotFound;
|
||||
[self.collectionView reloadData];
|
||||
NSString *tip = msg.length ? msg : KBLocalized(@"Failed to load products");
|
||||
if (tip.length) { [KBHUD showInfo:tip]; }
|
||||
return;
|
||||
}
|
||||
self.plans = products ?: @[];
|
||||
self.selectedIndex = self.plans.count > 0 ? 0 : NSNotFound;
|
||||
[self.collectionView reloadData];
|
||||
[self selectCurrentPlanAnimated:NO];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)selectCurrentPlanAnimated:(BOOL)animated {
|
||||
if (self.selectedIndex == NSNotFound) { return; }
|
||||
if (self.selectedIndex < 0 || self.selectedIndex >= self.plans.count) { return; }
|
||||
NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:1];
|
||||
if (!ip) { return; }
|
||||
// 系统层面也置为选中
|
||||
[self.collectionView selectItemAtIndexPath:ip animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||||
// 若此时 cell 不可见,willDisplay 再兜底
|
||||
[self.collectionView selectItemAtIndexPath:ip animated:animated scrollPosition:UICollectionViewScrollPositionNone];
|
||||
KBVipSubscribeCell *cell = (KBVipSubscribeCell *)[self.collectionView cellForItemAtIndexPath:ip];
|
||||
if (cell) { [cell applySelected:YES animated:NO]; }
|
||||
if ([cell isKindOfClass:KBVipSubscribeCell.class]) {
|
||||
[cell applySelected:YES animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (KBPayProductModel *)currentSelectedPlan {
|
||||
if (self.selectedIndex == NSNotFound) { return nil; }
|
||||
if (self.selectedIndex < 0 || self.selectedIndex >= self.plans.count) { return nil; }
|
||||
id plan = self.plans[self.selectedIndex];
|
||||
if (![plan isKindOfClass:KBPayProductModel.class]) { return nil; }
|
||||
return plan;
|
||||
}
|
||||
|
||||
- (NSString *)displayTitleForPlan:(KBPayProductModel *)plan {
|
||||
if (!plan) { return @""; }
|
||||
if (plan.productDescription.length) { return plan.productDescription; }
|
||||
NSString *name = plan.name ?: @"";
|
||||
NSString *unit = plan.unit ?: @"";
|
||||
if (name.length && unit.length) { return [NSString stringWithFormat:@"%@%@", name, unit]; }
|
||||
if (name.length) { return name; }
|
||||
if (unit.length) { return unit; }
|
||||
return KBLocalized(@"Subscription");
|
||||
}
|
||||
|
||||
#pragma mark - Header Height Calc
|
||||
@@ -126,8 +179,41 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
|
||||
#pragma mark - Bottom Actions
|
||||
- (void)onTapPayButton {
|
||||
// TODO: 接入支付,这里仅做 UI
|
||||
[KBHUD showInfo:KBLocalized(@"Pay clicked")];
|
||||
KBPayProductModel *plan = [self currentSelectedPlan];
|
||||
if (!plan) {
|
||||
[KBHUD showInfo:KBLocalized(@"Please select a product")];
|
||||
return;
|
||||
}
|
||||
NSString *productId = plan.productId;
|
||||
if (productId.length == 0) {
|
||||
[KBHUD showInfo:KBLocalized(@"Product unavailable")];
|
||||
return;
|
||||
}
|
||||
[KBHUD show];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.filter requestProductsWith:[NSSet setWithObject:productId] completion:^(NSArray<SKProduct *> * _Nonnull products) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self) { return; }
|
||||
SKProduct *match = nil;
|
||||
for (SKProduct *product in products) {
|
||||
if ([product.productIdentifier isEqualToString:productId]) {
|
||||
match = product;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
[KBHUD dismiss];
|
||||
[KBHUD showInfo:KBLocalized(@"Unable to load product information")];
|
||||
return;
|
||||
}
|
||||
[[FGIAPManager shared].iap buyProduct:match onCompletion:^(NSString * _Nonnull message, FGIAPManagerPurchaseRusult result) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[KBHUD dismiss];
|
||||
});
|
||||
}];
|
||||
});
|
||||
}];
|
||||
}
|
||||
- (void)agreementButtonAction{
|
||||
[KBHUD showInfo:KBLocalized(@"Open agreement")];
|
||||
@@ -149,9 +235,16 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
- (__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];
|
||||
if (indexPath.item < self.plans.count) {
|
||||
KBPayProductModel *plan = self.plans[indexPath.item];
|
||||
NSString *title = [self displayTitleForPlan:plan];
|
||||
NSString *price = [plan priceDisplayText];
|
||||
[cell configTitle:title price:price strike:nil];
|
||||
[cell applySelected:(indexPath.item == self.selectedIndex) animated:NO];
|
||||
} else {
|
||||
[cell configTitle:@"" price:@"" strike:nil];
|
||||
[cell applySelected:NO animated:NO];
|
||||
}
|
||||
return cell;
|
||||
} else {
|
||||
KBVipReviewListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipReviewListCellId forIndexPath:indexPath];
|
||||
@@ -169,7 +262,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
|
||||
#pragma mark - UICollectionView Delegate
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.section != 1) { return; }
|
||||
if (indexPath.section != 1 || indexPath.item >= self.plans.count) { return; }
|
||||
if (self.selectedIndex == indexPath.item) { return; }
|
||||
NSInteger old = self.selectedIndex;
|
||||
self.selectedIndex = indexPath.item;
|
||||
@@ -185,7 +278,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// 兜底:当订阅项第一次出现在屏幕上,强制同步选中样式
|
||||
if (indexPath.section == 1 && [cell isKindOfClass:KBVipSubscribeCell.class]) {
|
||||
if (indexPath.section == 1 && indexPath.item < self.plans.count && [cell isKindOfClass:KBVipSubscribeCell.class]) {
|
||||
BOOL sel = (indexPath.item == self.selectedIndex);
|
||||
KBVipSubscribeCell *c = (KBVipSubscribeCell *)cell;
|
||||
if (sel) {
|
||||
|
||||
@@ -30,6 +30,10 @@ typedef void(^KBPayProductsCompletion)(NSInteger sta,
|
||||
- (void)fetchInAppProductsNeedShow:(BOOL)needShow
|
||||
completion:(KBPayProductsCompletion)completion;
|
||||
|
||||
/// 查询订阅商品列表
|
||||
- (void)fetchSubscriptionProductsNeedShow:(BOOL)needShow
|
||||
completion:(KBPayProductsCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -32,9 +32,30 @@
|
||||
|
||||
- (void)fetchInAppProductsNeedShow:(BOOL)needShow
|
||||
completion:(KBPayProductsCompletion)completion {
|
||||
if (needShow) { [KBHUD show]; }
|
||||
NSDictionary *params = @{ @"type": @"in-app-purchase" };
|
||||
[[KBNetworkManager shared] GET:API_INAPP_PRODUCT_LIST
|
||||
[self fetchProductListWithPath:API_INAPP_PRODUCT_LIST
|
||||
params:params
|
||||
needShow:needShow
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)fetchSubscriptionProductsNeedShow:(BOOL)needShow
|
||||
completion:(KBPayProductsCompletion)completion {
|
||||
NSDictionary *params = @{ @"type": @"subscription" };
|
||||
[self fetchProductListWithPath:API_SUBSCRIPTION_PRODUCT_LIST
|
||||
params:params
|
||||
needShow:needShow
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (void)fetchProductListWithPath:(NSString *)path
|
||||
params:(NSDictionary *)params
|
||||
needShow:(BOOL)needShow
|
||||
completion:(KBPayProductsCompletion)completion {
|
||||
if (needShow) { [KBHUD show]; }
|
||||
[[KBNetworkManager shared] GET:path
|
||||
parameters:params
|
||||
headers:nil
|
||||
autoShowBusinessError:NO
|
||||
@@ -64,8 +85,6 @@
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
//+ (NSInteger)extractStatusFromResponseObject:(id)obj response:(NSURLResponse *)resp {
|
||||
// // 优先从 JSON 提取 code/status/success
|
||||
// if ([obj isKindOfClass:NSDictionary.class]) {
|
||||
|
||||
Reference in New Issue
Block a user