This commit is contained in:
2025-12-17 18:22:37 +08:00
parent 1e04e7c39a
commit 4a26419e67
4 changed files with 190 additions and 71 deletions

View File

@@ -7,6 +7,7 @@
#import "KBKeyboardSubscriptionProduct.h"
#import "KBNetworkManager.h"
#import "KBFullAccessManager.h"
#import "KBKeyboardSubscriptionFeatureItemView.h"
#import <MJExtension/MJExtension.h>
static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptionCellId";
@@ -19,7 +20,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
@interface KBKeyboardSubscriptionView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UIImageView *cardView;
@property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIScrollView *featureScrollView;
@property (nonatomic, strong) UIView *featureContentView;
@property (nonatomic, strong) UICollectionView *collectionView;
@@ -55,7 +55,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
- (void)layoutSubviews {
[super layoutSubviews];
self.featureLoopWidth = self.featureScrollView.contentSize.width * 0.5f;
[self startFeatureTickerIfNeeded];
}
@@ -103,7 +102,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
}];
[self.cardView addSubview:self.closeButton];
[self.cardView addSubview:self.titleLabel];
[self.cardView addSubview:self.featureScrollView];
[self.cardView addSubview:self.collectionView];
[self.cardView addSubview:self.purchaseButton];
@@ -114,25 +112,20 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.cardView.mas_left).offset(12);
make.top.equalTo(self.cardView.mas_top).offset(10);
make.top.equalTo(self.cardView.mas_top).offset(25);
make.width.height.mas_equalTo(28);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.closeButton.mas_centerY);
make.centerX.equalTo(self.cardView.mas_centerX);
}];
[self.featureScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.closeButton.mas_bottom).offset(8);
make.left.equalTo(self.cardView.mas_left).offset(12);
make.left.equalTo(self.closeButton.mas_right).offset(5);
make.centerY.equalTo(self.closeButton);
make.right.equalTo(self.cardView.mas_right).offset(-12);
make.height.mas_equalTo(54);
make.height.mas_equalTo(48);
}];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.featureScrollView);
make.top.equalTo(self.featureScrollView.mas_bottom).offset(12);
make.top.equalTo(self.featureScrollView.mas_bottom).offset(10);
make.height.mas_equalTo(138);
}];
@@ -173,28 +166,66 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
}
- (void)setupFeatureItems {
self.featureItems = @[
@{@"emoji": @"🤖", @"title": KBLocalized(@"Wireless Sub-ai Dialogue")},
@{@"emoji": @"⌨️", @"title": KBLocalized(@"Personalized Keyboard")},
@{@"emoji": @"🧠", @"title": KBLocalized(@"Creative Prompt Library")},
@{@"emoji": @"💬", @"title": KBLocalized(@"Chat Personalized Coach")}
NSArray *titles = @[
KBLocalized(@"Wireless Sub-ai\nDialogue"),
KBLocalized(@"Personalized\nKeyboard"),
KBLocalized(@"Chat\nPersona"),
KBLocalized(@"Emotional\nCounseling")
];
NSArray *images = @[
[UIImage imageNamed:@"home_ai_icon"] ?: [UIImage new],
[UIImage imageNamed:@"home_keyboard_icon"] ?: [UIImage new],
[UIImage imageNamed:@"home_chat_icon"] ?: [UIImage new],
[UIImage imageNamed:@"home_emotion_icon"] ?: [UIImage new]
];
NSMutableArray *items = [NSMutableArray array];
[titles enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
UIImage *img = (idx < images.count) ? images[idx] : nil;
if (!obj) { obj = @""; }
if (!img) { img = [UIImage new]; }
[items addObject:@{@"title": obj, @"image": img}];
}];
self.featureItems = [items copy];
[self rebuildFeatureBadges];
}
- (void)rebuildFeatureBadges {
[self.featureContentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
NSArray *loopData = [self.featureItems arrayByAddingObjectsFromArray:self.featureItems];
UIView *previous = nil;
if (self.featureItems.count == 0) {
self.featureScrollView.contentSize = CGSizeZero;
self.featureLoopWidth = 0;
[self stopFeatureTicker];
return;
}
BOOL shouldLoop = (self.featureItems.count > 1);
CGFloat spacing = 12.0;
for (NSDictionary *info in loopData) {
UIView *badge = [self buildBadgeWithEmoji:info[@"emoji"] title:info[@"title"]];
NSInteger baseCount = self.featureItems.count;
NSMutableArray<NSNumber *> *baseWidths = [NSMutableArray arrayWithCapacity:baseCount];
CGFloat baseTotalWidth = 0.0;
for (NSInteger i = 0; i < baseCount; i++) {
NSDictionary *info = self.featureItems[i];
NSString *title = [info[@"title"] isKindOfClass:NSString.class] ? info[@"title"] : @"";
CGFloat width = [KBKeyboardSubscriptionFeatureItemView preferredWidthForTitle:title];
[baseWidths addObject:@(width)];
baseTotalWidth += width;
if (i > 0) { baseTotalWidth += spacing; }
}
NSArray *loopData = shouldLoop ? [self.featureItems arrayByAddingObjectsFromArray:self.featureItems] : self.featureItems;
CGFloat totalWidth = shouldLoop ? (baseTotalWidth * 2 + spacing) : baseTotalWidth;
UIView *previous = nil;
for (NSInteger idx = 0; idx < loopData.count; idx++) {
NSDictionary *info = loopData[idx];
UIImage *img = [info[@"image"] isKindOfClass:UIImage.class] ? info[@"image"] : nil;
NSString *title = [info[@"title"] isKindOfClass:NSString.class] ? info[@"title"] : @"";
CGFloat width = (baseCount > 0) ? baseWidths[(NSUInteger)(idx % baseCount)].doubleValue : 120.0;
KBKeyboardSubscriptionFeatureItemView *badge = [[KBKeyboardSubscriptionFeatureItemView alloc] init];
[badge configureWithImage:(img ?: [UIImage new]) title:title];
[self.featureContentView addSubview:badge];
CGFloat textWidth = [info[@"title"] boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 24)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:13 weight:UIFontWeightSemibold]}
context:nil].size.width;
CGFloat width = MIN(MAX(textWidth + 60, 150), 240);
[badge mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(self.featureContentView);
make.width.mas_equalTo(width);
@@ -206,6 +237,7 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
}];
previous = badge;
}
__weak typeof(self) weakSelf = self;
[self.featureContentView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(weakSelf.featureScrollView);
@@ -217,37 +249,21 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
make.right.equalTo(weakSelf.featureScrollView);
}
}];
}
- (UIView *)buildBadgeWithEmoji:(NSString *)emoji title:(NSString *)title {
UIView *container = [[UIView alloc] init];
container.layer.cornerRadius = 24;
container.layer.masksToBounds = YES;
container.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.85];
UILabel *emojiLabel = [[UILabel alloc] init];
emojiLabel.text = emoji ?: @"✨";
emojiLabel.font = [UIFont systemFontOfSize:20];
UILabel *textLabel = [[UILabel alloc] init];
textLabel.text = title ?: @"";
textLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightSemibold];
textLabel.textColor = [UIColor colorWithHex:0x4A4A4A];
[container addSubview:emojiLabel];
[container addSubview:textLabel];
[emojiLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(container.mas_left).offset(14);
make.centerY.equalTo(container.mas_centerY);
}];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(emojiLabel.mas_right).offset(10);
make.centerY.equalTo(container.mas_centerY);
make.right.equalTo(container.mas_right).offset(-14);
}];
return container;
CGFloat minWidth = CGRectGetWidth(self.featureScrollView.bounds);
if (minWidth <= 0) { minWidth = [UIScreen mainScreen].bounds.size.width - 24; }
CGFloat height = CGRectGetHeight(self.featureScrollView.bounds);
if (height <= 0) { height = 48; }
CGFloat contentWidth = totalWidth;
if (contentWidth <= minWidth) {
contentWidth = minWidth;
self.featureLoopWidth = 0;
[self stopFeatureTicker];
self.featureScrollView.contentOffset = CGPointZero;
} else {
self.featureLoopWidth = shouldLoop ? (baseTotalWidth + spacing) : 0.0;
[self startFeatureTickerIfNeeded];
}
self.featureScrollView.contentSize = CGSizeMake(contentWidth, height);
}
#pragma mark - Actions
@@ -423,10 +439,10 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
- (UIImageView *)cardView {
if (!_cardView) {
_cardView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"keybord_bg_icon"]];
// _cardView.layer.cornerRadius = 20;
// _cardView.layer.masksToBounds = YES;
_cardView.layer.cornerRadius = 20;
_cardView.layer.masksToBounds = YES;
_cardView.userInteractionEnabled = YES;
_cardView.contentMode = UIViewContentModeScaleAspectFill;
_cardView.clipsToBounds = true;
}
return _cardView;
}
@@ -445,16 +461,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
return _closeButton;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.text = KBLocalized(@"Premium Subscription");
_titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_titleLabel.textColor = [UIColor colorWithHex:0x24556B];
}
return _titleLabel;
}
- (UIScrollView *)featureScrollView {
if (!_featureScrollView) {
_featureScrollView = [[UIScrollView alloc] init];