// // KBKeyboardSubscriptionView.m // CustomKeyboard // #import "KBKeyboardSubscriptionView.h" #import "KBKeyboardSubscriptionProduct.h" #import "KBNetworkManager.h" #import "KBFullAccessManager.h" #import "KBKeyboardSubscriptionFeatureItemView.h" #import static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptionCellId"; @interface KBKeyboardSubscriptionOptionCell : UICollectionViewCell - (void)configureWithProduct:(KBKeyboardSubscriptionProduct *)product; - (void)applySelected:(BOOL)selected animated:(BOOL)animated; @end @interface KBKeyboardSubscriptionView () @property (nonatomic, strong) UIImageView *cardView; @property (nonatomic, strong) UIButton *closeButton; @property (nonatomic, strong) UIScrollView *featureScrollView; @property (nonatomic, strong) UIView *featureContentView; @property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) UIButton *purchaseButton; @property (nonatomic, strong) UILabel *agreementLabel; @property (nonatomic, strong) UIButton *agreementButton; @property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator; @property (nonatomic, strong) UILabel *emptyLabel; @property (nonatomic, copy) NSArray *featureItems; @property (nonatomic, copy) NSArray *products; @property (nonatomic, assign) NSInteger selectedIndex; @property (nonatomic, assign) BOOL didLoadOnce; @property (nonatomic, assign, getter=isLoading) BOOL loading; @property (nonatomic, strong) CADisplayLink *featureDisplayLink; @property (nonatomic, assign) CGFloat featureLoopWidth; @end @implementation KBKeyboardSubscriptionView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor clearColor]; _selectedIndex = NSNotFound; [self setupCardView]; [self setupFeatureItems]; } return self; } - (void)dealloc { [self.featureDisplayLink invalidate]; } - (void)layoutSubviews { [super layoutSubviews]; [self startFeatureTickerIfNeeded]; } - (void)didMoveToWindow { [super didMoveToWindow]; if (self.window) { [self startFeatureTickerIfNeeded]; } else { [self stopFeatureTicker]; } } - (void)setHidden:(BOOL)hidden { BOOL oldHidden = self.isHidden; [super setHidden:hidden]; if (oldHidden == hidden) { return; } if (hidden) { [self stopFeatureTicker]; } else if (self.window) { [self startFeatureTickerIfNeeded]; } } #pragma mark - Public - (void)refreshProductsIfNeeded { if (!self.didLoadOnce) { [self fetchProducts]; } } - (void)reloadProducts { [self fetchProducts]; } #pragma mark - UI - (void)setupCardView { [self addSubview:self.cardView]; [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.mas_left).offset(0); make.right.equalTo(self.mas_right).offset(0); make.top.equalTo(self.mas_top).offset(0); make.bottom.equalTo(self.mas_bottom).offset(0); }]; [self.cardView addSubview:self.closeButton]; [self.cardView addSubview:self.featureScrollView]; [self.cardView addSubview:self.collectionView]; [self.cardView addSubview:self.purchaseButton]; [self.cardView addSubview:self.agreementLabel]; [self.cardView addSubview:self.agreementButton]; [self.cardView addSubview:self.loadingIndicator]; [self.cardView addSubview:self.emptyLabel]; [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.cardView.mas_left).offset(12); make.top.equalTo(self.cardView.mas_top).offset(25); make.width.height.mas_equalTo(28); }]; [self.featureScrollView mas_makeConstraints:^(MASConstraintMaker *make) { 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(48); }]; [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.featureScrollView); make.top.equalTo(self.featureScrollView.mas_bottom).offset(10); make.height.mas_equalTo(138); }]; [self.agreementButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.cardView.mas_centerX); make.bottom.equalTo(self.cardView.mas_bottom).offset(-14); }]; [self.agreementLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.cardView.mas_centerX); make.bottom.equalTo(self.agreementButton.mas_top).offset(-4); }]; [self.purchaseButton mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.cardView.mas_left).offset(24); make.right.equalTo(self.cardView.mas_right).offset(-24); make.top.equalTo(self.collectionView.mas_bottom).offset(20); make.bottom.equalTo(self.agreementLabel.mas_top).offset(-16); make.height.mas_greaterThanOrEqualTo(@48); }]; [self.loadingIndicator mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.collectionView); }]; [self.emptyLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.collectionView); }]; [self.featureScrollView addSubview:self.featureContentView]; [self.featureContentView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.bottom.equalTo(self.featureScrollView); make.left.equalTo(self.featureScrollView); make.height.equalTo(self.featureScrollView); make.right.equalTo(self.featureScrollView); }]; [self updatePurchaseButtonState]; } - (void)setupFeatureItems { 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)]; 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; NSInteger baseCount = self.featureItems.count; NSMutableArray *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]; [badge mas_makeConstraints:^(MASConstraintMaker *make) { make.top.bottom.equalTo(self.featureContentView); make.width.mas_equalTo(width); if (previous) { make.left.equalTo(previous.mas_right).offset(spacing); } else { make.left.equalTo(self.featureContentView.mas_left); } }]; previous = badge; } __weak typeof(self) weakSelf = self; [self.featureContentView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.bottom.equalTo(weakSelf.featureScrollView); make.left.equalTo(weakSelf.featureScrollView); make.height.equalTo(weakSelf.featureScrollView); if (previous) { make.right.equalTo(previous.mas_right); } else { make.right.equalTo(weakSelf.featureScrollView); } }]; 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 - (void)onTapClose { if ([self.delegate respondsToSelector:@selector(subscriptionViewDidTapClose:)]) { [self.delegate subscriptionViewDidTapClose:self]; } } - (void)onTapPurchase { if (self.selectedIndex == NSNotFound || self.selectedIndex >= self.products.count) { [KBHUD showInfo:KBLocalized(@"Please select a product")]; return; } KBKeyboardSubscriptionProduct *product = self.products[self.selectedIndex]; if ([self.delegate respondsToSelector:@selector(subscriptionView:didTapPurchaseForProduct:)]) { [self.delegate subscriptionView:self didTapPurchaseForProduct:product]; } } - (void)onTapAgreement { [KBHUD showInfo:KBLocalized(@"Agreement coming soon")]; } #pragma mark - Data - (void)fetchProducts { if (self.isLoading) { return; } if (![[KBFullAccessManager shared] hasFullAccess]) { [KBHUD showInfo:KBLocalized(@"Enable Full Access to continue")]; return; } self.loading = YES; self.emptyLabel.hidden = YES; [self.loadingIndicator startAnimating]; NSDictionary *params = @{@"type": @"subscription"}; __weak typeof(self) weakSelf = self; [[KBNetworkManager shared] GET:API_SUBSCRIPTION_PRODUCT_LIST parameters:params headers:nil completion:^(NSDictionary *json, NSURLResponse *response, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ __strong typeof(weakSelf) self = weakSelf; if (!self) { return; } self.loading = NO; [self.loadingIndicator stopAnimating]; if (error) { NSString *tip = error.localizedDescription ?: KBLocalized(@"Network error"); [KBHUD showInfo:tip]; self.products = @[]; self.selectedIndex = NSNotFound; [self.collectionView reloadData]; self.emptyLabel.hidden = NO; [self updatePurchaseButtonState]; return; } id dataObj = json[@"data"]; if (![dataObj isKindOfClass:[NSArray class]]) { dataObj = json[@"list"]; } if (![dataObj isKindOfClass:[NSArray class]]) { self.products = @[]; self.selectedIndex = NSNotFound; [self.collectionView reloadData]; self.emptyLabel.hidden = NO; [self updatePurchaseButtonState]; return; } NSArray *models = [KBKeyboardSubscriptionProduct mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj]; self.products = models ?: @[]; self.selectedIndex = self.products.count > 0 ? 0 : NSNotFound; self.emptyLabel.hidden = self.products.count > 0; [self.collectionView reloadData]; [self selectCurrentProductAnimated:NO]; [self updatePurchaseButtonState]; self.didLoadOnce = YES; }); }]; } - (void)selectCurrentProductAnimated:(BOOL)animated { if (self.selectedIndex == NSNotFound || self.selectedIndex >= self.products.count) { return; } NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.selectedIndex inSection:0]; [self.collectionView selectItemAtIndexPath:indexPath animated:animated scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; KBKeyboardSubscriptionOptionCell *cell = (KBKeyboardSubscriptionOptionCell *)[self.collectionView cellForItemAtIndexPath:indexPath]; if ([cell isKindOfClass:KBKeyboardSubscriptionOptionCell.class]) { [cell applySelected:YES animated:animated]; } } - (void)updatePurchaseButtonState { BOOL enabled = (self.products.count > 0); self.purchaseButton.enabled = enabled; self.purchaseButton.alpha = enabled ? 1.0 : 0.5; } #pragma mark - Feature ticker - (void)startFeatureTickerIfNeeded { if (self.featureDisplayLink || self.featureLoopWidth <= 0) { return; } self.featureDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleFeatureTick)]; self.featureDisplayLink.preferredFramesPerSecond = 60; [self.featureDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } - (void)stopFeatureTicker { [self.featureDisplayLink invalidate]; self.featureDisplayLink = nil; } - (void)handleFeatureTick { if (self.featureLoopWidth <= 0) { return; } CGFloat nextX = self.featureScrollView.contentOffset.x + 0.35f; if (nextX >= self.featureLoopWidth) { nextX -= self.featureLoopWidth; } self.featureScrollView.contentOffset = CGPointMake(nextX, 0); } #pragma mark - UICollectionView DataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.products.count; } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { KBKeyboardSubscriptionOptionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBKeyboardSubscriptionCellId forIndexPath:indexPath]; if (indexPath.item < self.products.count) { KBKeyboardSubscriptionProduct *product = self.products[indexPath.item]; [cell configureWithProduct:product]; BOOL selected = (indexPath.item == self.selectedIndex); [cell applySelected:selected animated:NO]; } else { [cell configureWithProduct:nil]; [cell applySelected:NO animated:NO]; } return cell; } #pragma mark - UICollectionView Delegate - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.item >= self.products.count) { return; } NSInteger previous = self.selectedIndex; self.selectedIndex = indexPath.item; if (previous != NSNotFound && previous != indexPath.item) { NSIndexPath *prev = [NSIndexPath indexPathForItem:previous inSection:0]; KBKeyboardSubscriptionOptionCell *prevCell = (KBKeyboardSubscriptionOptionCell *)[collectionView cellForItemAtIndexPath:prev]; [prevCell applySelected:NO animated:YES]; } KBKeyboardSubscriptionOptionCell *cell = (KBKeyboardSubscriptionOptionCell *)[collectionView cellForItemAtIndexPath:indexPath]; [cell applySelected:YES animated:YES]; } #pragma mark - Layout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat width = MIN(MAX(collectionView.bounds.size.width * 0.56, 150), 220); return CGSizeMake(width, collectionView.bounds.size.height - 12); } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 12.0; } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(6, 4, 6, 4); } #pragma mark - Lazy - (UIImageView *)cardView { if (!_cardView) { _cardView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"keybord_bg_icon"]]; _cardView.layer.cornerRadius = 20; _cardView.layer.masksToBounds = YES; _cardView.userInteractionEnabled = YES; _cardView.contentMode = UIViewContentModeScaleAspectFill; } return _cardView; } - (UIButton *)closeButton { if (!_closeButton) { _closeButton = [UIButton buttonWithType:UIButtonTypeSystem]; _closeButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.7]; _closeButton.layer.cornerRadius = 14; _closeButton.layer.masksToBounds = YES; [_closeButton setTitle:@"✕" forState:UIControlStateNormal]; [_closeButton setTitleColor:[UIColor colorWithHex:0x666666] forState:UIControlStateNormal]; _closeButton.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; [_closeButton addTarget:self action:@selector(onTapClose) forControlEvents:UIControlEventTouchUpInside]; } return _closeButton; } - (UIScrollView *)featureScrollView { if (!_featureScrollView) { _featureScrollView = [[UIScrollView alloc] init]; _featureScrollView.showsHorizontalScrollIndicator = NO; _featureScrollView.scrollEnabled = NO; _featureScrollView.clipsToBounds = YES; _featureScrollView.layer.cornerRadius = 26; } return _featureScrollView; } - (UIView *)featureContentView { if (!_featureContentView) { _featureContentView = [[UIView alloc] init]; } return _featureContentView; } - (UICollectionView *)collectionView { if (!_collectionView) { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; 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:KBKeyboardSubscriptionOptionCell.class forCellWithReuseIdentifier:kKBKeyboardSubscriptionCellId]; } return _collectionView; } - (UIButton *)purchaseButton { if (!_purchaseButton) { _purchaseButton = [UIButton buttonWithType:UIButtonTypeSystem]; _purchaseButton.layer.cornerRadius = 26; _purchaseButton.layer.masksToBounds = YES; [_purchaseButton setTitle:KBLocalized(@"Recharge Now") forState:UIControlStateNormal]; _purchaseButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; [_purchaseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [_purchaseButton setBackgroundImage:[self imageWithColor:[UIColor colorWithHex:0x02BEAC]] forState:UIControlStateNormal]; [_purchaseButton addTarget:self action:@selector(onTapPurchase) forControlEvents:UIControlEventTouchUpInside]; } return _purchaseButton; } - (UILabel *)agreementLabel { if (!_agreementLabel) { _agreementLabel = [[UILabel alloc] init]; _agreementLabel.text = KBLocalized(@"By clicking \"pay\", you agree to the"); _agreementLabel.font = [UIFont systemFontOfSize:11]; _agreementLabel.textColor = [UIColor colorWithHex:0x4A4A4A]; } return _agreementLabel; } - (UIButton *)agreementButton { if (!_agreementButton) { _agreementButton = [UIButton buttonWithType:UIButtonTypeSystem]; [_agreementButton setTitle:KBLocalized(@"Membership Agreement") forState:UIControlStateNormal]; _agreementButton.titleLabel.font = [UIFont systemFontOfSize:11 weight:UIFontWeightSemibold]; [_agreementButton setTitleColor:[UIColor colorWithHex:0x02BEAC] forState:UIControlStateNormal]; [_agreementButton addTarget:self action:@selector(onTapAgreement) forControlEvents:UIControlEventTouchUpInside]; } return _agreementButton; } - (UIActivityIndicatorView *)loadingIndicator { if (!_loadingIndicator) { _loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium]; _loadingIndicator.hidesWhenStopped = YES; } return _loadingIndicator; } - (UILabel *)emptyLabel { if (!_emptyLabel) { _emptyLabel = [[UILabel alloc] init]; _emptyLabel.text = KBLocalized(@"No products available"); _emptyLabel.font = [UIFont systemFontOfSize:13]; _emptyLabel.textColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; _emptyLabel.textAlignment = NSTextAlignmentCenter; _emptyLabel.hidden = YES; } return _emptyLabel; } - (UIImage *)imageWithColor:(UIColor *)color { CGSize size = CGSizeMake(1, 1); UIGraphicsBeginImageContextWithOptions(size, NO, 0); [color setFill]; UIRectFill(CGRectMake(0, 0, size.width, size.height)); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } @end #pragma mark - Cell @interface KBKeyboardSubscriptionOptionCell () @property (nonatomic, strong) UIView *cardView; @property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, strong) UILabel *priceLabel; @property (nonatomic, strong) UILabel *strikeLabel; @property (nonatomic, strong) UIView *checkBadge; @property (nonatomic, strong) UILabel *checkLabel; @end @implementation KBKeyboardSubscriptionOptionCell - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.contentView.backgroundColor = [UIColor clearColor]; [self.contentView addSubview:self.cardView]; [self.cardView addSubview:self.titleLabel]; [self.cardView addSubview:self.priceLabel]; [self.cardView addSubview:self.strikeLabel]; [self.cardView addSubview:self.checkBadge]; [self.checkBadge addSubview:self.checkLabel]; [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.contentView); }]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.cardView.mas_top).offset(12); make.left.equalTo(self.cardView.mas_left).offset(14); make.right.equalTo(self.cardView.mas_right).offset(-14); }]; [self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.titleLabel.mas_left); make.top.equalTo(self.titleLabel.mas_bottom).offset(10); }]; [self.strikeLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.priceLabel.mas_left); make.top.equalTo(self.priceLabel.mas_bottom).offset(4); }]; [self.checkBadge mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.cardView.mas_centerX); make.bottom.equalTo(self.cardView.mas_bottom).offset(-8); make.width.height.mas_equalTo(20); }]; [self.checkLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.checkBadge); }]; } return self; } - (void)prepareForReuse { [super prepareForReuse]; self.titleLabel.text = @""; self.priceLabel.text = @""; self.strikeLabel.attributedText = nil; self.strikeLabel.hidden = YES; [self applySelected:NO animated:NO]; } - (void)configureWithProduct:(KBKeyboardSubscriptionProduct *)product { if (!product) { return; } self.titleLabel.text = [product displayTitle]; self.priceLabel.text = [product priceDisplayText]; NSString *strike = [product strikePriceDisplayText]; if (strike.length > 0) { NSDictionary *attr = @{ NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle), NSForegroundColorAttributeName: [[UIColor blackColor] colorWithAlphaComponent:0.35], NSFontAttributeName: [UIFont systemFontOfSize:12] }; self.strikeLabel.attributedText = [[NSAttributedString alloc] initWithString:strike attributes:attr]; self.strikeLabel.hidden = NO; } else { self.strikeLabel.attributedText = nil; self.strikeLabel.hidden = YES; } } - (void)applySelected:(BOOL)selected animated:(BOOL)animated { void (^changes)(void) = ^{ self.cardView.layer.borderColor = selected ? [UIColor colorWithHex:0x02BEAC].CGColor : [[UIColor blackColor] colorWithAlphaComponent:0.12].CGColor; self.cardView.layer.borderWidth = selected ? 2.0 : 1.0; self.checkBadge.backgroundColor = selected ? [UIColor colorWithHex:0x02BEAC] : [[UIColor blackColor] colorWithAlphaComponent:0.15]; self.checkLabel.textColor = [UIColor whiteColor]; }; if (animated) { [UIView animateWithDuration:0.18 animations:changes]; } else { changes(); } } - (UIView *)cardView { if (!_cardView) { _cardView = [[UIView alloc] init]; _cardView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.96]; _cardView.layer.cornerRadius = 20; _cardView.layer.borderWidth = 1.0; _cardView.layer.borderColor = [[UIColor blackColor] colorWithAlphaComponent:0.12].CGColor; } return _cardView; } - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [[UILabel alloc] init]; _titleLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightSemibold]; _titleLabel.numberOfLines = 2; _titleLabel.textColor = [UIColor colorWithHex:0x2F2F2F]; } return _titleLabel; } - (UILabel *)priceLabel { if (!_priceLabel) { _priceLabel = [[UILabel alloc] init]; _priceLabel.font = [UIFont systemFontOfSize:22 weight:UIFontWeightBold]; _priceLabel.textColor = [UIColor colorWithHex:0x232323]; } return _priceLabel; } - (UILabel *)strikeLabel { if (!_strikeLabel) { _strikeLabel = [[UILabel alloc] init]; _strikeLabel.hidden = YES; } return _strikeLabel; } - (UIView *)checkBadge { if (!_checkBadge) { _checkBadge = [[UIView alloc] init]; _checkBadge.layer.cornerRadius = 10; _checkBadge.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.15]; } return _checkBadge; } - (UILabel *)checkLabel { if (!_checkLabel) { _checkLabel = [[UILabel alloc] init]; _checkLabel.text = @"✓"; _checkLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold]; _checkLabel.textColor = [UIColor whiteColor]; _checkLabel.textAlignment = NSTextAlignmentCenter; } return _checkLabel; } @end