1
This commit is contained in:
@@ -26,7 +26,8 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if (self = [super init]) {
|
||||
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
||||
_timeout = 10.0;
|
||||
_defaultHeaders = @{ @"Accept": @"application/json" };
|
||||
// 默认接受任意类型,避免下载图片/二进制被服务端基于 Accept 拒绝
|
||||
_defaultHeaders = @{ @"Accept": @"*/*" };
|
||||
// 设置基础域名,路径可相对该地址拼接
|
||||
_baseURL = [NSURL URLWithString:KB_BASE_URL];
|
||||
}
|
||||
@@ -45,10 +46,15 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
// 使用 AFHTTPRequestSerializer 生成带参数与头的请求
|
||||
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
|
||||
serializer.timeoutInterval = self.timeout;
|
||||
NSError *serror = nil;
|
||||
NSMutableURLRequest *req = [serializer requestWithMethod:@"GET"
|
||||
URLString:urlString
|
||||
parameters:parameters
|
||||
error:NULL];
|
||||
error:&serror];
|
||||
if (serror || !req) {
|
||||
if (completion) completion(nil, nil, serror ?: [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey: @"无效的URL"}]);
|
||||
return nil;
|
||||
}
|
||||
[self applyHeaders:headers toMutableRequest:req contentType:nil];
|
||||
return [self startAFTaskWithRequest:req completion:completion];
|
||||
}
|
||||
@@ -109,7 +115,11 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
// 响应先用原始数据返回,再按 Content-Type 解析 JSON(与原实现一致)
|
||||
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
|
||||
if (error) { if (completion) completion(nil, response, error); return; }
|
||||
// AFN 默认对非 2xx 的状态码返回 error;这里直接回调上层
|
||||
if (error) {
|
||||
if (completion) completion(nil, response, error);
|
||||
return;
|
||||
}
|
||||
NSData *data = (NSData *)responseObject;
|
||||
if (![data isKindOfClass:[NSData class]]) {
|
||||
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:@"无数据"}]);
|
||||
@@ -119,7 +129,19 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
ct = ((NSHTTPURLResponse *)response).allHeaderFields[@"Content-Type"];
|
||||
}
|
||||
BOOL looksJSON = (ct && [ct.lowercaseString containsString:@"application/json"]);
|
||||
// 更宽松的 JSON 判定:Content-Type 里包含 json;或首字符是 { / [
|
||||
BOOL looksJSON = (ct && [[ct lowercaseString] containsString:@"json"]);
|
||||
if (!looksJSON) {
|
||||
// 内容嗅探(不依赖服务端声明)
|
||||
const unsigned char *bytes = data.bytes;
|
||||
NSUInteger len = data.length;
|
||||
for (NSUInteger i = 0; !looksJSON && i < len; i++) {
|
||||
unsigned char c = bytes[i];
|
||||
if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue;
|
||||
looksJSON = (c == '{' || c == '[');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (looksJSON) {
|
||||
NSError *jsonErr = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
|
||||
@@ -139,8 +161,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if (!_manager) {
|
||||
NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
cfg.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
||||
cfg.timeoutIntervalForRequest = self.timeout;
|
||||
cfg.timeoutIntervalForResource = MAX(self.timeout, 30.0);
|
||||
// 不在会话级别设置超时,避免与 per-request 的 serializer.timeoutInterval 产生不一致
|
||||
if (@available(iOS 11.0, *)) { cfg.waitsForConnectivity = YES; }
|
||||
_manager = [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:cfg];
|
||||
// 默认不使用 JSON 解析器,保持原生数据,再按需解析
|
||||
|
||||
22
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/Contents.json
vendored
Normal file
22
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "recharge_btn_bg@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "recharge_btn_bg@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/recharge_btn_bg@2x.png
vendored
Normal file
BIN
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/recharge_btn_bg@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/recharge_btn_bg@3x.png
vendored
Normal file
BIN
keyBoard/Assets.xcassets/Shop/recharge_btn_bg.imageset/recharge_btn_bg@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -15,6 +15,16 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
- (void)fail:(KBNetworkError)code completion:(KBNetworkCompletion)completion;
|
||||
@end
|
||||
|
||||
#if DEBUG
|
||||
// 在使用到 Debug 辅助方法之前做前置声明,避免出现
|
||||
// “No visible @interface declares the selector …” 的编译错误。
|
||||
@interface KBNetworkManager (Debug)
|
||||
- (NSString *)kb_prettyJSONStringFromObject:(id)obj;
|
||||
- (NSString *)kb_textFromData:(NSData *)data;
|
||||
- (NSString *)kb_trimmedString:(NSString *)s maxLength:(NSUInteger)maxLen;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation KBNetworkManager
|
||||
|
||||
+ (instancetype)shared {
|
||||
@@ -26,7 +36,8 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if (self = [super init]) {
|
||||
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
||||
_timeout = 10.0;
|
||||
_defaultHeaders = @{ @"Accept": @"application/json" };
|
||||
// 默认接受任意类型,避免下载图片/二进制被服务端基于 Accept 拒绝
|
||||
_defaultHeaders = @{ @"Accept": @"*/*" };
|
||||
// 设置基础域名,路径可相对该地址拼接
|
||||
_baseURL = [NSURL URLWithString:KB_BASE_URL];
|
||||
}
|
||||
@@ -45,11 +56,24 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
// 使用 AFHTTPRequestSerializer 生成带参数与头的请求
|
||||
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
|
||||
serializer.timeoutInterval = self.timeout;
|
||||
NSError *serror = nil;
|
||||
NSMutableURLRequest *req = [serializer requestWithMethod:@"GET"
|
||||
URLString:urlString
|
||||
parameters:parameters
|
||||
error:NULL];
|
||||
error:&serror];
|
||||
if (serror || !req) {
|
||||
if (completion) completion(nil, nil, serror ?: [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey: @"无效的URL"}]);
|
||||
return nil;
|
||||
}
|
||||
[self applyHeaders:headers toMutableRequest:req contentType:nil];
|
||||
#if DEBUG
|
||||
// 打印请求信息(GET)
|
||||
NSString *paramStr = [self kb_prettyJSONStringFromObject:parameters] ?: @"(null)";
|
||||
KBLOG(@"HTTP GET\nURL: %@\nHeaders: %@\n参数: %@",
|
||||
req.URL.absoluteString,
|
||||
req.allHTTPHeaderFields ?: @{},
|
||||
paramStr);
|
||||
#endif
|
||||
return [self startAFTaskWithRequest:req completion:completion];
|
||||
}
|
||||
|
||||
@@ -70,6 +94,14 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
error:&error];
|
||||
if (error) { if (completion) completion(nil, nil, error); return nil; }
|
||||
[self applyHeaders:headers toMutableRequest:req contentType:nil];
|
||||
#if DEBUG
|
||||
// 打印请求信息(POST JSON)
|
||||
NSString *bodyStr = [self kb_prettyJSONStringFromObject:jsonBody] ?: @"(null)";
|
||||
KBLOG(@"HTTP POST\nURL: %@\nHeaders: %@\nJSON: %@",
|
||||
req.URL.absoluteString,
|
||||
req.allHTTPHeaderFields ?: @{},
|
||||
bodyStr);
|
||||
#endif
|
||||
return [self startAFTaskWithRequest:req completion:completion];
|
||||
}
|
||||
|
||||
@@ -109,9 +141,24 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
// 响应先用原始数据返回,再按 Content-Type 解析 JSON(与原实现一致)
|
||||
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
|
||||
if (error) { if (completion) completion(nil, response, error); return; }
|
||||
// AFN 默认对非 2xx 的状态码返回 error;这里先日志,再直接回调上层
|
||||
if (error) {
|
||||
#if DEBUG
|
||||
NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0;
|
||||
KBLOG(@"请求失败\nURL: %@\n状态: %ld\n错误: %@\nUserInfo: %@",
|
||||
req.URL.absoluteString,
|
||||
(long)status,
|
||||
error.localizedDescription,
|
||||
error.userInfo ?: @{});
|
||||
#endif
|
||||
if (completion) completion(nil, response, error);
|
||||
return;
|
||||
}
|
||||
NSData *data = (NSData *)responseObject;
|
||||
if (![data isKindOfClass:[NSData class]]) {
|
||||
#if DEBUG
|
||||
KBLOG(@"无效响应\nURL: %@\n说明: %@", req.URL.absoluteString, @"未获取到数据");
|
||||
#endif
|
||||
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:@"无数据"}]);
|
||||
return;
|
||||
}
|
||||
@@ -119,13 +166,53 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
ct = ((NSHTTPURLResponse *)response).allHeaderFields[@"Content-Type"];
|
||||
}
|
||||
BOOL looksJSON = (ct && [ct.lowercaseString containsString:@"application/json"]);
|
||||
// 更宽松的 JSON 判定:Content-Type 里包含 json;或首字符是 { / [
|
||||
BOOL looksJSON = (ct && [[ct lowercaseString] containsString:@"json"]);
|
||||
if (!looksJSON) {
|
||||
// 内容嗅探(不依赖服务端声明)
|
||||
const unsigned char *bytes = data.bytes;
|
||||
NSUInteger len = data.length;
|
||||
for (NSUInteger i = 0; !looksJSON && i < len; i++) {
|
||||
unsigned char c = bytes[i];
|
||||
if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue;
|
||||
looksJSON = (c == '{' || c == '[');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (looksJSON) {
|
||||
NSError *jsonErr = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
|
||||
if (jsonErr) { if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:@"JSON解析失败"}]); return; }
|
||||
if (jsonErr) {
|
||||
#if DEBUG
|
||||
KBLOG(@"响应解析失败(JSON)\nURL: %@\n错误: %@",
|
||||
req.URL.absoluteString,
|
||||
jsonErr.localizedDescription);
|
||||
#endif
|
||||
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:@"JSON解析失败"}]);
|
||||
return;
|
||||
}
|
||||
#if DEBUG
|
||||
NSString *pretty = [self kb_prettyJSONStringFromObject:json] ?: @"(null)";
|
||||
pretty = [self kb_trimmedString:pretty maxLength:4096];
|
||||
NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0;
|
||||
KBLOG(@"响应成功(JSON)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@",
|
||||
req.URL.absoluteString,
|
||||
(long)status,
|
||||
ct ?: @"",
|
||||
pretty);
|
||||
#endif
|
||||
if (completion) completion(json, response, nil);
|
||||
} else {
|
||||
#if DEBUG
|
||||
NSString *text = [self kb_textFromData:data];
|
||||
text = [self kb_trimmedString:text maxLength:4096];
|
||||
NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0;
|
||||
KBLOG(@"响应成功(Data)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@",
|
||||
req.URL.absoluteString,
|
||||
(long)status,
|
||||
ct ?: @"",
|
||||
text);
|
||||
#endif
|
||||
if (completion) completion(data, response, nil);
|
||||
}
|
||||
}];
|
||||
@@ -139,8 +226,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
if (!_manager) {
|
||||
NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
cfg.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
||||
cfg.timeoutIntervalForRequest = self.timeout;
|
||||
cfg.timeoutIntervalForResource = MAX(self.timeout, 30.0);
|
||||
// 不在会话级别设置超时,避免与 per-request 的 serializer.timeoutInterval 产生不一致
|
||||
if (@available(iOS 11.0, *)) { cfg.waitsForConnectivity = YES; }
|
||||
_manager = [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:cfg];
|
||||
// 默认不使用 JSON 解析器,保持原生数据,再按需解析
|
||||
@@ -167,3 +253,47 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Debug helpers
|
||||
|
||||
#if DEBUG
|
||||
@implementation KBNetworkManager (Debug)
|
||||
|
||||
// 将对象转为漂亮的 JSON 字符串;否则返回 description
|
||||
- (NSString *)kb_prettyJSONStringFromObject:(id)obj {
|
||||
if (!obj || obj == (id)kCFNull) return nil;
|
||||
if (![NSJSONSerialization isValidJSONObject:obj]) {
|
||||
// 非标准 JSON 对象,直接 description
|
||||
return [obj description];
|
||||
}
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:NSJSONWritingPrettyPrinted error:NULL];
|
||||
if (!data) return [obj description];
|
||||
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ?: [obj description];
|
||||
}
|
||||
|
||||
// 尝试把二进制数据转为可读文本;失败则返回占位带长度
|
||||
- (NSString *)kb_textFromData:(NSData *)data {
|
||||
if (!data) return @"(null)";
|
||||
if (data.length == 0) return @"";
|
||||
// 优先 UTF-8
|
||||
NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (text.length > 0) return text;
|
||||
// 再尝试常见编码
|
||||
NSArray *encs = @[@(NSASCIIStringEncoding), @(NSISOLatin1StringEncoding), @(NSUnicodeStringEncoding)];
|
||||
for (NSNumber *n in encs) {
|
||||
text = [[NSString alloc] initWithData:data encoding:n.unsignedIntegerValue];
|
||||
if (text.length > 0) return text;
|
||||
}
|
||||
return [NSString stringWithFormat:@"<binary %lu bytes>", (unsigned long)data.length];
|
||||
}
|
||||
|
||||
// 过长时裁剪,避免刷屏
|
||||
- (NSString *)kb_trimmedString:(NSString *)s maxLength:(NSUInteger)maxLen {
|
||||
if (!s) return @"";
|
||||
if (s.length <= maxLen) return s;
|
||||
NSString *head = [s substringToIndex:maxLen];
|
||||
return [head stringByAppendingString:@"\n...<trimmed>..."];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
//
|
||||
|
||||
#import "KBShopHeadView.h"
|
||||
@interface KBShopHeadView()
|
||||
|
||||
@end
|
||||
@implementation KBShopHeadView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// 懒加载 collectionView,并添加到视图
|
||||
[self.view addSubview:self.collectionView];
|
||||
@@ -133,7 +133,7 @@
|
||||
UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
_collectionView.backgroundColor = [UIColor whiteColor];
|
||||
_collectionView.backgroundColor = [UIColor clearColor];
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
// 注册皮肤卡片 cell
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#import <JXPagingView/JXPagerView.h>
|
||||
#import <MJRefresh/MJRefresh.h>
|
||||
#import "KBShopItemVC.h"
|
||||
#import "KBSearchVC.h"
|
||||
#import "MySkinVC.h"
|
||||
|
||||
|
||||
#import "KBWebViewViewController.h"
|
||||
|
||||
@@ -31,6 +34,10 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
@property (nonatomic, strong) JXCategoryTitleView *categoryView;
|
||||
@property (nonatomic, strong) NSArray <NSString *> *titles;
|
||||
@property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图
|
||||
@property (nonatomic, strong) UIButton *searchBtn;
|
||||
@property (nonatomic, strong) UIButton *skinBtn;
|
||||
// 记录当前分类条是否为白底,避免重复设置
|
||||
@property (nonatomic, assign) BOOL categoryIsWhite;
|
||||
|
||||
@end
|
||||
|
||||
@@ -45,6 +52,18 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
[self setupUI];
|
||||
[self.view addSubview:self.skinBtn];
|
||||
[self.view addSubview:self.searchBtn];
|
||||
[self.skinBtn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.view).offset(-16);
|
||||
make.top.mas_equalTo(KB_NAV_TOTAL_HEIGHT - 25);
|
||||
make.width.height.mas_equalTo(25);
|
||||
}];
|
||||
[self.searchBtn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.skinBtn.mas_left).offset(-16);
|
||||
make.top.equalTo(self.skinBtn);
|
||||
make.width.height.mas_equalTo(25);
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
@@ -62,7 +81,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
_userHeaderView = [[KBShopHeadView alloc] init];
|
||||
_categoryView = (JXCategoryTitleView *)[[KBCategoryTitleView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, JXheightForHeaderInSection)];
|
||||
self.categoryView.titles = self.titles;
|
||||
self.categoryView.backgroundColor = [UIColor whiteColor];
|
||||
self.categoryView.backgroundColor = [UIColor clearColor];
|
||||
self.categoryView.delegate = self;
|
||||
self.categoryView.titleSelectedColor = [UIColor colorWithHex:0x1B1F1A];
|
||||
self.categoryView.titleColor = [UIColor colorWithHex:0x9F9F9F];
|
||||
@@ -102,7 +121,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
[self.view addSubview:self.pagerView];
|
||||
// self.pagerView.listContainerView.scrollView.scrollEnabled = false;
|
||||
|
||||
|
||||
// self.pagerView.listContainerView.listCellBackgroundColor = [UIColor clearColor];
|
||||
self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;
|
||||
|
||||
//导航栏隐藏的情况,处理扣边返回,下面的代码要加上
|
||||
@@ -200,10 +219,50 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidScroll:(UIScrollView *)scrollView {
|
||||
CGFloat thresholdDistance = JXTableHeaderViewHeight;
|
||||
CGFloat percent = scrollView.contentOffset.y/thresholdDistance;
|
||||
// 正确的吸顶阈值:头图高度 - 固定区偏移(即导航高度)
|
||||
CGFloat headerH = (CGFloat)[self tableHeaderViewHeightInPagerView:self.pagerView];
|
||||
CGFloat thresholdDistance = MAX(0.0, headerH - self.pagerView.pinSectionHeaderVerticalOffset);
|
||||
CGFloat percent = (thresholdDistance > 0 ? scrollView.contentOffset.y/thresholdDistance : 1);
|
||||
percent = MAX(0, MIN(1, percent));
|
||||
self.naviBGView.alpha = percent;
|
||||
// 分类条背景:未吸顶时透明,吸顶后白色
|
||||
// 触发吸顶的大致阈值 ≈ 头图高度 - 顶部偏移(此处用头图高度近似即可)
|
||||
BOOL shouldWhite = (thresholdDistance > 0.0 && scrollView.contentOffset.y >= (thresholdDistance - 0.5));
|
||||
if (shouldWhite != self.categoryIsWhite) {
|
||||
self.categoryIsWhite = shouldWhite;
|
||||
UIColor *bg = shouldWhite ? [UIColor whiteColor] : [UIColor clearColor];
|
||||
self.categoryView.backgroundColor = bg;
|
||||
// 内部 collectionView 也同步,避免出现条纹底色
|
||||
self.categoryView.collectionView.backgroundColor = bg;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - action
|
||||
- (void)searchBtnAction{
|
||||
KBSearchVC *vc = [[KBSearchVC alloc] init];
|
||||
// [self.navigationController pushViewController:vc animated:true];
|
||||
}
|
||||
|
||||
- (void)skinBtnAction{
|
||||
MySkinVC *vc = [[MySkinVC alloc] init];
|
||||
// [self.navigationController pushViewController:vc animated:true];
|
||||
}
|
||||
|
||||
#pragma mark - lazy
|
||||
- (UIButton *)searchBtn{
|
||||
if (!_searchBtn) {
|
||||
_searchBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_searchBtn setImage:[UIImage imageNamed:@"shop_search_icon"] forState:UIControlStateNormal];
|
||||
[_searchBtn addTarget:self action:@selector(searchBtnAction) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _searchBtn;
|
||||
}
|
||||
- (UIButton *)skinBtn{
|
||||
if (!_skinBtn) {
|
||||
_skinBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_skinBtn setImage:[UIImage imageNamed:@"shop_skin_icon"] forState:UIControlStateNormal];
|
||||
[_skinBtn addTarget:self action:@selector(skinBtnAction) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _skinBtn;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -41,6 +41,16 @@
|
||||
|
||||
|
||||
//-----------------------------------------------宏定义全局----------------------------------------------------------/
|
||||
// 调试专用日志(DEBUG 打印,RELEASE 不打印)。尽量显眼,包含函数与行号。
|
||||
#if DEBUG
|
||||
#define KBLOG(fmt, ...) do { \
|
||||
NSString *kb_msg__ = [NSString stringWithFormat:(fmt), ##__VA_ARGS__]; \
|
||||
NSLog(@"\n==============================[KB DEBUG]==============================\n[Function] %s\n[Line] %d\n%@\n=====================================================================\n", __PRETTY_FUNCTION__, __LINE__, kb_msg__); \
|
||||
} while(0)
|
||||
#else
|
||||
#define KBLOG(...)
|
||||
#endif
|
||||
|
||||
// 通用链接(Universal Links)统一配置
|
||||
// 仅需修改这里的域名/前缀,工程内所有使用 UL 的地方都会同步。
|
||||
#define KB_UL_BASE @"https://app.tknb.net/ul" // 与 Associated Domains 一致
|
||||
|
||||
Reference in New Issue
Block a user