2025-10-29 14:28:57 +08:00
|
|
|
|
//
|
|
|
|
|
|
// BaseTableView.m
|
|
|
|
|
|
// keyBoard
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#import "BaseTableView.h"
|
|
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
// 可选引入:若未集成 DZNEmptyDataSet,此处不会编译进来
|
|
|
|
|
|
#if __has_include(<DZNEmptyDataSet/UIScrollView+EmptyDataSet.h>)
|
|
|
|
|
|
#import <DZNEmptyDataSet/UIScrollView+EmptyDataSet.h>
|
|
|
|
|
|
#define KB_HAS_DZN 1
|
|
|
|
|
|
#else
|
|
|
|
|
|
#define KB_HAS_DZN 0
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-11-09 21:41:35 +08:00
|
|
|
|
// 可选引入:LYEmptyView(通过 Pod 集成),用于空态视图的“自动显隐+属性配置”方案
|
|
|
|
|
|
#if __has_include(<LYEmptyView/UIView+Empty.h>)
|
|
|
|
|
|
#import <LYEmptyView/UIView+Empty.h>
|
|
|
|
|
|
#import <LYEmptyView/LYEmptyView.h>
|
|
|
|
|
|
#define KB_HAS_LY 1
|
|
|
|
|
|
#else
|
|
|
|
|
|
#define KB_HAS_LY 0
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
@interface BaseTableView ()
|
|
|
|
|
|
#if KB_HAS_DZN
|
|
|
|
|
|
<DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
2025-10-29 14:28:57 +08:00
|
|
|
|
@implementation BaseTableView
|
|
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
|
|
|
|
|
|
if (self = [super initWithFrame:frame style:style]) {
|
|
|
|
|
|
[self commonInit];
|
|
|
|
|
|
}
|
|
|
|
|
|
return self;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
|
2025-10-29 14:28:57 +08:00
|
|
|
|
- (void)commonInit {
|
2025-10-29 14:49:35 +08:00
|
|
|
|
// iOS 15+ 头部默认留白,统一归零(老系统无此属性,不会影响)
|
2025-10-29 14:28:57 +08:00
|
|
|
|
self.sectionHeaderTopPadding = 0;
|
|
|
|
|
|
self.backgroundColor = [UIColor whiteColor];
|
|
|
|
|
|
self.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
|
|
|
|
|
self.estimatedRowHeight = 0;
|
|
|
|
|
|
self.estimatedSectionHeaderHeight = 0;
|
|
|
|
|
|
self.estimatedSectionFooterHeight = 0;
|
|
|
|
|
|
self.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
2025-10-29 14:49:35 +08:00
|
|
|
|
|
|
|
|
|
|
// 空数据占位默认配置(保持“活”的:可开可关)
|
|
|
|
|
|
_useEmptyDataSet = YES; // 默认开启
|
|
|
|
|
|
_emptyShouldAllowScroll = YES; // 默认允许滚动
|
|
|
|
|
|
_emptyVerticalOffset = 0; // 默认不偏移
|
|
|
|
|
|
_emptyTitleText = @"暂无数据"; // 默认标题
|
|
|
|
|
|
|
|
|
|
|
|
#if KB_HAS_DZN
|
|
|
|
|
|
self.emptyDataSetSource = self;
|
|
|
|
|
|
self.emptyDataSetDelegate = self;
|
|
|
|
|
|
#endif
|
2025-10-29 14:28:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
#pragma mark - Public
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_reloadEmptyDataSet {
|
|
|
|
|
|
#if KB_HAS_DZN
|
|
|
|
|
|
[self reloadEmptyDataSet];
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setUseEmptyDataSet:(BOOL)useEmptyDataSet {
|
|
|
|
|
|
_useEmptyDataSet = useEmptyDataSet;
|
|
|
|
|
|
#if KB_HAS_DZN
|
|
|
|
|
|
// 切换时,动态挂/卸源委托
|
|
|
|
|
|
self.emptyDataSetSource = useEmptyDataSet ? (id<DZNEmptyDataSetSource>)self : nil;
|
|
|
|
|
|
self.emptyDataSetDelegate = useEmptyDataSet ? (id<DZNEmptyDataSetDelegate>)self : nil;
|
|
|
|
|
|
[self reloadEmptyDataSet];
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - DZNEmptyDataSet (仅当库存在时编译)
|
|
|
|
|
|
#if KB_HAS_DZN
|
|
|
|
|
|
|
|
|
|
|
|
// 是否展示空视图
|
|
|
|
|
|
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView {
|
|
|
|
|
|
return self.useEmptyDataSet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 标题
|
|
|
|
|
|
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
|
|
|
|
|
|
NSString *title = self.emptyTitleText ?: @"";
|
|
|
|
|
|
NSDictionary *attrs = @{ NSFontAttributeName : [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold],
|
|
|
|
|
|
NSForegroundColorAttributeName : [UIColor colorWithWhite:0.5 alpha:1.0] };
|
|
|
|
|
|
return [[NSAttributedString alloc] initWithString:title attributes:attrs];
|
|
|
|
|
|
}
|
2025-10-29 14:28:57 +08:00
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
// 描述
|
|
|
|
|
|
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView {
|
|
|
|
|
|
if (self.emptyDescriptionText.length == 0) return nil;
|
|
|
|
|
|
NSDictionary *attrs = @{ NSFontAttributeName : [UIFont systemFontOfSize:14],
|
|
|
|
|
|
NSForegroundColorAttributeName : [UIColor colorWithWhite:0.6 alpha:1.0] };
|
|
|
|
|
|
return [[NSAttributedString alloc] initWithString:self.emptyDescriptionText attributes:attrs];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 图片
|
|
|
|
|
|
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
|
|
|
|
|
|
return self.emptyImage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按钮标题
|
|
|
|
|
|
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
|
|
|
|
|
|
if (self.emptyButtonTitle.length == 0) return nil;
|
|
|
|
|
|
UIColor *color = (state == UIControlStateHighlighted) ? [UIColor darkTextColor] : [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0];
|
|
|
|
|
|
NSDictionary *attrs = @{ NSFontAttributeName : [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold],
|
|
|
|
|
|
NSForegroundColorAttributeName : color };
|
|
|
|
|
|
return [[NSAttributedString alloc] initWithString:self.emptyButtonTitle attributes:attrs];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 垂直位置偏移
|
|
|
|
|
|
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView {
|
|
|
|
|
|
return self.emptyVerticalOffset;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 行为
|
|
|
|
|
|
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView { return self.emptyShouldAllowScroll; }
|
|
|
|
|
|
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView { return YES; }
|
|
|
|
|
|
|
|
|
|
|
|
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view {
|
|
|
|
|
|
if (self.emptyDidTapView) self.emptyDidTapView();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
|
|
|
|
|
|
if (self.emptyDidTapButton) self.emptyDidTapButton();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-11-09 21:41:35 +08:00
|
|
|
|
// MARK: - LYEmptyView 快速挂载(与 DZN 二选一)
|
|
|
|
|
|
- (void)kb_makeDefaultEmptyViewWithImage:(UIImage *)image
|
|
|
|
|
|
title:(NSString *)title
|
|
|
|
|
|
detail:(NSString *)detail
|
|
|
|
|
|
buttonTitle:(NSString *)buttonTitle
|
|
|
|
|
|
tapHandler:(KBEmptyAction)tapHandler
|
|
|
|
|
|
buttonHandler:(KBEmptyAction)buttonHandler {
|
|
|
|
|
|
#if KB_HAS_LY
|
|
|
|
|
|
// 关闭 DZN,避免两套空态叠加
|
|
|
|
|
|
self.useEmptyDataSet = NO;
|
|
|
|
|
|
|
|
|
|
|
|
// 默认文案
|
|
|
|
|
|
NSString *t = title ?: @"暂无数据";
|
|
|
|
|
|
|
|
|
|
|
|
LYEmptyView *ev = nil;
|
|
|
|
|
|
if (buttonTitle.length > 0) {
|
|
|
|
|
|
// 带按钮(优先 block 方式)
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
ev = [LYEmptyView emptyActionViewWithImage:image
|
|
|
|
|
|
titleStr:t
|
|
|
|
|
|
detailStr:detail ?: @""
|
|
|
|
|
|
btnTitleStr:buttonTitle
|
|
|
|
|
|
btnClickBlock:^{ if (buttonHandler) buttonHandler(); [weakSelf setNeedsLayout]; }];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 无按钮
|
|
|
|
|
|
ev = [LYEmptyView emptyViewWithImage:image titleStr:t detailStr:detail ?: @""];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 点击空白区域
|
|
|
|
|
|
if (tapHandler) {
|
|
|
|
|
|
ev.tapEmptyViewBlock = ^{ tapHandler(); };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 统一样式(可按需微调)
|
|
|
|
|
|
ev.emptyViewIsCompleteCoverSuperView = NO; // 不全屏覆盖,按内容大小布局
|
|
|
|
|
|
ev.ignoreContentInset = YES; // 忽略 inset,避免导航/安全区影响
|
|
|
|
|
|
ev.contentViewOffset = -10; // 轻微上移
|
|
|
|
|
|
ev.subViewMargin = 14; // 子控件间距
|
|
|
|
|
|
ev.titleLabFont = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
|
|
|
|
|
|
ev.titleLabTextColor = [UIColor colorWithWhite:0.45 alpha:1.0];
|
|
|
|
|
|
ev.detailLabFont = [UIFont systemFontOfSize:14];
|
|
|
|
|
|
ev.detailLabTextColor = [UIColor colorWithWhite:0.6 alpha:1.0];
|
|
|
|
|
|
ev.detailLabLineSpacing = 3;
|
|
|
|
|
|
ev.detailLabMaxLines = 2;
|
|
|
|
|
|
ev.actionBtnFont = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
|
|
|
|
|
|
ev.actionBtnCornerRadius = 6;
|
|
|
|
|
|
ev.actionBtnBorderWidth = 1;
|
|
|
|
|
|
ev.actionBtnBorderColor = [UIColor colorWithWhite:0.88 alpha:1.0];
|
|
|
|
|
|
ev.actionBtnTitleColor = [UIColor colorWithRed:0.22 green:0.49 blue:0.96 alpha:1.0];
|
|
|
|
|
|
ev.actionBtnBackGroundColor = [UIColor whiteColor];
|
|
|
|
|
|
ev.actionBtnHorizontalMargin = 20; // 自动宽度:文字 + padding*2
|
|
|
|
|
|
|
|
|
|
|
|
// 自动显隐交由控制层(网络请求)
|
|
|
|
|
|
ev.autoShowEmptyView = NO;
|
|
|
|
|
|
|
|
|
|
|
|
self.ly_emptyView = (LYEmptyView *)ev;
|
|
|
|
|
|
#else
|
|
|
|
|
|
// 未集成 LYEmptyView 时不做任何处理,保持兼容
|
|
|
|
|
|
(void)image; (void)title; (void)detail; (void)buttonTitle; (void)tapHandler; (void)buttonHandler;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 14:49:35 +08:00
|
|
|
|
@end
|