- 行程一级页面

- 创建行程UI   30%
This commit is contained in:
linshujie 2026-06-23 18:34:12 +08:00
parent e235bc59a4
commit b7c96e6fc6
104 changed files with 22837 additions and 12300 deletions

View File

@ -46,6 +46,7 @@ target 'QuickLocation' do
pod 'SDCycleScrollView'
pod 'lottie-ios'
pod 'TagListView', '~> 1.0'
pod 'BRPickerView'
#第三方SDk
pod 'GYSDK' #个推一键登录

View File

@ -6,6 +6,16 @@ PODS:
- AMapFoundation-NO-IDFA (>= 1.9.0)
- AMapNavi-NO-IDFA (11.2.000):
- AMapFoundation-NO-IDFA (>= 1.9.0)
- BRPickerView (3.0.0):
- BRPickerView/Default (= 3.0.0)
- BRPickerView/Core (3.0.0)
- BRPickerView/DatePicker (3.0.0):
- BRPickerView/Core
- BRPickerView/Default (3.0.0):
- BRPickerView/DatePicker
- BRPickerView/TextPicker
- BRPickerView/TextPicker (3.0.0):
- BRPickerView/Core
- CocoaLumberjack/Core (3.9.1)
- CocoaLumberjack/Swift (3.9.1):
- CocoaLumberjack/Core
@ -178,6 +188,7 @@ DEPENDENCIES:
- AlipaySDK-iOS
- AMapLocation-NO-IDFA
- AMapNavi-NO-IDFA
- BRPickerView
- CocoaLumberjack/Swift
- CocoaMQTT
- GYSDK
@ -220,6 +231,7 @@ SPEC REPOS:
- AMapFoundation-NO-IDFA
- AMapLocation-NO-IDFA
- AMapNavi-NO-IDFA
- BRPickerView
- CocoaLumberjack
- CocoaMQTT
- Differentiator
@ -284,6 +296,7 @@ SPEC CHECKSUMS:
AMapFoundation-NO-IDFA: a2e3c895398d7ee757278e1a0a8f9359da4b146e
AMapLocation-NO-IDFA: 7cd8fc837ea41edfbf4d937cd20572e277b77d18
AMapNavi-NO-IDFA: d55b9b138c6e540f737c689975cae2c9dbd1026a
BRPickerView: cd2e0e3aa87af062bc7a1b6f78d3dfd5f04f04bb
CocoaLumberjack: e4ba3b414dfca8c1916c6303d37f63b3a95134c6
CocoaMQTT: 1e2fa493d57045d66fb1eba2bb5ffb8d77039c60
Differentiator: e8497ceab83c1b10ca233716d547b9af21b9344d
@ -334,6 +347,6 @@ SPEC CHECKSUMS:
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
PODFILE CHECKSUM: 4d254b7f0c48f2a2100703f9ac600d5d826b586a
PODFILE CHECKSUM: 848cd9ba02b2f1c209ac9eef708dd2c43b005385
COCOAPODS: 1.16.2

16
Pods/BRPickerView/BRPickerView/BRPickerView.h generated Executable file
View File

@ -0,0 +1,16 @@
//
// BRPickerView.h
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#ifndef BRPickerView_h
#define BRPickerView_h
#import "BRDatePickerView.h"
#import "BRTextPickerView.h"
#endif /* BRPickerView_h */

View File

@ -0,0 +1,73 @@
//
// BRPickerAlertView.h
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import <UIKit/UIKit.h>
#import "BRPickerStyle.h"
NS_ASSUME_NONNULL_BEGIN
typedef void(^BRPickerAlertCancelBlock)(void);
typedef void(^BRPickerAlertDoneBlock)(void);
@interface BRPickerAlertView : UIView
/** 选择器标题 */
@property (nullable, nonatomic, copy) NSString *title;
/** 自定义UI样式不传或为nil时是默认样式 */
@property (nullable, nonatomic, strong) BRPickerStyle *pickerStyle;
/** accessory view for above picker view. default is nil */
@property (nullable, nonatomic, strong) UIView *pickerHeaderView;
/** accessory view below picker view. default is nil */
@property (nullable, nonatomic, strong) UIView *pickerFooterView;
/** 取消选择的回调 */
@property (nullable, nonatomic, copy) BRPickerAlertCancelBlock cancelBlock;
/// 确定按钮点击事件的回调
/// 应用场景:如果是自定义确定按钮,需要在该按钮点击事件方法里,执行一下 doneBlock 回调。目的是触发组件内部执行 resultBlock 回调,回调选择的值
@property (nullable, nonatomic, copy) BRPickerAlertDoneBlock doneBlock;
/** 弹框视图(使用场景:可以在 alertView 上添加选择器的自定义背景视图) */
@property (nullable, nonatomic, strong) UIView *alertView;
/** 组件的父视图:可以传 自己获取的 keyWindow或页面的 view */
@property (nullable, nonatomic, strong) UIView *keyView;
/// 刷新选择器数据
/// 应用场景:动态更新数据源、动态更新选择的值、选择器类型切换等
- (void)reloadData;
/// 扩展一:添加选择器到指定容器视图上
/// 应用场景:可将中间的滚轮选择器 pickerView 视图(不包含蒙层及标题栏)添加到任何自定义视图上(会自动填满容器视图),也方便自定义更多的弹框样式
/// 补充说明:如果是自定义确定按钮,需要回调默认选择的值:只需在自定义确定按钮的点击事件方法里执行一下 doneBlock 回调(目的是去触发组件内部执行 resultBlock 回调,进而回调默认选择的值)
/// @param view 容器视图
- (void)addPickerToView:(nullable UIView *)view NS_REQUIRES_SUPER;
/// 从指定容器视图上移除选择器
/// @param view 容器视图
- (void)removePickerFromView:(nullable UIView *)view;
/// 扩展二添加自定义视图到选择器pickerView
/// 应用场景:可以添加一些固定的标题、单位等到选择器中间
/// @param customView 自定义视图
- (void)addSubViewToPicker:(UIView *)customView;
/// 扩展三添加自定义视图到标题栏titleBarView
/// 应用场景:可以添加一些子控件到标题栏
/// @param customView 自定义视图
- (void)addSubViewToTitleBar:(UIView *)customView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,398 @@
//
// BRPickerAlertView.m
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "BRPickerAlertView.h"
@interface BRPickerAlertView ()
//
@property (nonatomic, strong) UIView *maskBgView;
//
@property (nonatomic, strong) UIView *titleBarView;
//
@property (nonatomic, strong) UIButton *cancelBtn;
//
@property (nonatomic, strong) UIButton *doneBtn;
//
@property (nonatomic, strong) UILabel *titleLabel;
//
@property (nonatomic, assign) CGFloat cancelBtnMargin;
//
@property (nonatomic, assign) CGFloat doneBtnMargin;
@end
@implementation BRPickerAlertView
- (void)initUI {
self.frame = self.keyView.bounds;
//
self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if (!self.pickerStyle.hiddenMaskView) {
[self addSubview:self.maskBgView];
}
[self addSubview:self.alertView];
//
if (!self.pickerStyle.hiddenTitleBarView) {
[self.alertView addSubview:self.titleBarView];
[self.alertView sendSubviewToBack:self.titleBarView];
if (!self.pickerStyle.hiddenTitleLabel) {
[self.titleBarView addSubview:self.titleLabel];
}
if (!self.pickerStyle.hiddenCancelBtn) {
[self.titleBarView addSubview:self.cancelBtn];
//
if (self.pickerStyle.cancelBtnFrame.origin.x < self.bounds.size.width / 2) {
self.cancelBtnMargin = self.pickerStyle.cancelBtnFrame.origin.x;
} else {
self.cancelBtnMargin = self.bounds.size.width - self.pickerStyle.cancelBtnFrame.origin.x - self.pickerStyle.cancelBtnFrame.size.width;
}
}
if (!self.pickerStyle.hiddenDoneBtn) {
[self.titleBarView addSubview:self.doneBtn];
//
if (self.pickerStyle.doneBtnFrame.origin.x < self.bounds.size.width / 2) {
self.doneBtnMargin = self.pickerStyle.doneBtnFrame.origin.x;
} else {
self.doneBtnMargin = self.bounds.size.width - self.pickerStyle.doneBtnFrame.origin.x - self.pickerStyle.doneBtnFrame.size.width;
}
}
}
}
#pragma mark -
- (void)layoutSubviews {
[super layoutSubviews];
if (_cancelBtn || _doneBtn) {
if (@available(iOS 11.0, *)) {
UIEdgeInsets safeInsets = self.safeAreaInsets;
if (_cancelBtn) {
CGRect cancelBtnFrame = self.pickerStyle.cancelBtnFrame;
if (cancelBtnFrame.origin.x < MIN(self.bounds.size.width / 2, self.bounds.size.height / 2)) {
cancelBtnFrame.origin.x += safeInsets.left;
} else {
cancelBtnFrame.origin.x = self.bounds.size.width - cancelBtnFrame.size.width - safeInsets.right - self.cancelBtnMargin;
}
self.cancelBtn.frame = cancelBtnFrame;
}
if (_doneBtn) {
CGRect doneBtnFrame = self.pickerStyle.doneBtnFrame;
if (doneBtnFrame.origin.x < MIN(self.bounds.size.width / 2, self.bounds.size.height / 2)) {
doneBtnFrame.origin.x += safeInsets.left;
} else {
doneBtnFrame.origin.x = self.bounds.size.width - doneBtnFrame.size.width - safeInsets.right - self.doneBtnMargin;
}
self.doneBtn.frame = doneBtnFrame;
}
}
}
if (_alertView && self.pickerStyle.topCornerRadius > 0) {
//
[BRPickerStyle br_setView:_alertView roundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight withRadius:self.pickerStyle.topCornerRadius];
}
}
#pragma mark -
- (UIView *)maskBgView {
if (!_maskBgView) {
_maskBgView = [[UIView alloc]initWithFrame:self.keyView.bounds];
_maskBgView.backgroundColor = self.pickerStyle.maskColor;
//
_maskBgView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_maskBgView.userInteractionEnabled = YES;
UITapGestureRecognizer *myTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(didTapMaskBgView:)];
[_maskBgView addGestureRecognizer:myTap];
}
return _maskBgView;
}
#pragma mark -
- (UIView *)alertView {
if (!_alertView) {
CGFloat accessoryViewHeight = 0;
if (self.pickerHeaderView) {
accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
}
if (self.pickerFooterView) {
accessoryViewHeight += self.pickerFooterView.bounds.size.height;
}
CGFloat height = self.pickerStyle.titleBarHeight + self.pickerStyle.pickerHeight + self.pickerStyle.paddingBottom + accessoryViewHeight;
_alertView = [[UIView alloc]initWithFrame:CGRectMake(0, self.keyView.bounds.size.height - height, self.keyView.bounds.size.width, height)];
_alertView.backgroundColor = self.pickerStyle.alertViewColor ? self.pickerStyle.alertViewColor : self.pickerStyle.pickerColor;
if (!self.pickerStyle.topCornerRadius && !self.pickerStyle.hiddenShadowLine) {
// 线
UIView *shadowLineView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, _alertView.frame.size.width, self.pickerStyle.shadowLineHeight)];
shadowLineView.backgroundColor = self.pickerStyle.shadowLineColor;
shadowLineView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_alertView addSubview:shadowLineView];
}
_alertView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
}
return _alertView;
}
#pragma mark -
- (UIView *)titleBarView {
if (!_titleBarView) {
_titleBarView =[[UIView alloc]initWithFrame:CGRectMake(0, 0, self.keyView.bounds.size.width, self.pickerStyle.titleBarHeight)];
_titleBarView.backgroundColor = self.pickerStyle.titleBarColor;
_titleBarView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
if (!self.pickerStyle.hiddenTitleLine) {
// 线
UIView *titleLineView = [[UIView alloc]initWithFrame:CGRectMake(self.pickerStyle.titleLineLeftSpace, _titleBarView.frame.size.height - self.pickerStyle.titleLineHeight, _titleBarView.frame.size.width - self.pickerStyle.titleLineLeftSpace - self.pickerStyle.titleLineRightSpace, self.pickerStyle.titleLineHeight)];
titleLineView.backgroundColor = self.pickerStyle.titleLineColor;
titleLineView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_titleBarView addSubview:titleLineView];
}
}
return _titleBarView;
}
#pragma mark -
- (UIButton *)cancelBtn {
if (!_cancelBtn) {
_cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_cancelBtn.frame = self.pickerStyle.cancelBtnFrame;
_cancelBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
_cancelBtn.backgroundColor = self.pickerStyle.cancelColor;;
_cancelBtn.titleLabel.font = self.pickerStyle.cancelTextFont;
[_cancelBtn setTitleColor:self.pickerStyle.cancelTextColor forState:UIControlStateNormal];
if (self.pickerStyle.cancelBtnImage) {
[_cancelBtn setImage:self.pickerStyle.cancelBtnImage forState:UIControlStateNormal];
}
if (self.pickerStyle.cancelBtnTitle) {
[_cancelBtn setTitle:self.pickerStyle.cancelBtnTitle forState:UIControlStateNormal];
}
[_cancelBtn addTarget:self action:@selector(clickCancelBtn) forControlEvents:UIControlEventTouchUpInside];
//
if (self.pickerStyle.cancelBorderStyle == BRBorderStyleSolid) {
_cancelBtn.layer.cornerRadius = self.pickerStyle.cancelCornerRadius > 0 ? self.pickerStyle.cancelCornerRadius : 6.0f;
_cancelBtn.layer.borderColor = self.pickerStyle.cancelTextColor.CGColor;
_cancelBtn.layer.borderWidth = self.pickerStyle.cancelBorderWidth > 0 ? self.pickerStyle.cancelBorderWidth : 1.0f;
_cancelBtn.layer.masksToBounds = YES;
} else if (self.pickerStyle.cancelBorderStyle == BRBorderStyleFill) {
_cancelBtn.layer.cornerRadius = self.pickerStyle.cancelCornerRadius > 0 ? self.pickerStyle.cancelCornerRadius : 6.0f;
_cancelBtn.layer.masksToBounds = YES;
}
}
return _cancelBtn;
}
#pragma mark -
- (UIButton *)doneBtn {
if (!_doneBtn) {
_doneBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_doneBtn.frame = self.pickerStyle.doneBtnFrame;
_doneBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
_doneBtn.backgroundColor = self.pickerStyle.doneColor;
if (self.pickerStyle.doneBtnImage) {
[_doneBtn setImage:self.pickerStyle.doneBtnImage forState:UIControlStateNormal];
}
if (self.pickerStyle.doneBtnTitle) {
_doneBtn.titleLabel.font = self.pickerStyle.doneTextFont;
[_doneBtn setTitleColor:self.pickerStyle.doneTextColor forState:UIControlStateNormal];
[_doneBtn setTitle:self.pickerStyle.doneBtnTitle forState:UIControlStateNormal];
}
[_doneBtn addTarget:self action:@selector(clickDoneBtn) forControlEvents:UIControlEventTouchUpInside];
//
if (self.pickerStyle.doneBorderStyle == BRBorderStyleSolid) {
_doneBtn.layer.cornerRadius = self.pickerStyle.doneCornerRadius > 0 ? self.pickerStyle.doneCornerRadius : 6.0f;
_doneBtn.layer.borderColor = self.pickerStyle.doneTextColor.CGColor;
_doneBtn.layer.borderWidth = self.pickerStyle.doneBorderWidth > 0 ? self.pickerStyle.doneBorderWidth : 1.0f;
_doneBtn.layer.masksToBounds = YES;
} else if (self.pickerStyle.doneBorderStyle == BRBorderStyleFill) {
_doneBtn.layer.cornerRadius = self.pickerStyle.doneCornerRadius > 0 ? self.pickerStyle.doneCornerRadius : 6.0f;
_doneBtn.layer.masksToBounds = YES;
}
}
return _doneBtn;
}
#pragma mark - label
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc]initWithFrame:self.pickerStyle.titleLabelFrame];
_titleLabel.backgroundColor = self.pickerStyle.titleLabelColor;
_titleLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.font = self.pickerStyle.titleTextFont;
_titleLabel.textColor = self.pickerStyle.titleTextColor;
_titleLabel.text = self.title;
}
return _titleLabel;
}
#pragma mark -
- (void)didTapMaskBgView:(UITapGestureRecognizer *)sender {
[self removePickerFromView:nil];
if (self.cancelBlock) {
self.cancelBlock();
}
}
#pragma mark -
- (void)clickCancelBtn {
if (self.cancelBlock) {
self.cancelBlock();
}
[self removePickerFromView:nil];
}
#pragma mark -
- (void)clickDoneBtn {
if (self.doneBlock) {
self.doneBlock();
}
[self removePickerFromView:nil];
}
#pragma mark -
- (void)addPickerToView:(UIView *)view {
if (view) {
self.frame = view.bounds;
self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
CGFloat accessoryViewHeight = 0;
if (self.pickerHeaderView) {
CGRect rect = self.pickerHeaderView.frame;
self.pickerHeaderView.frame = CGRectMake(0, 0, view.bounds.size.width, rect.size.height);
self.pickerHeaderView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self addSubview:self.pickerHeaderView];
accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
}
if (self.pickerFooterView) {
CGRect rect = self.pickerFooterView.frame;
self.pickerFooterView.frame = CGRectMake(0, view.bounds.size.height - rect.size.height, view.bounds.size.width, rect.size.height);
self.pickerFooterView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self addSubview:self.pickerFooterView];
accessoryViewHeight += self.pickerFooterView.bounds.size.height;
}
[view addSubview:self];
} else {
[self initUI];
if (self.pickerHeaderView) {
CGRect rect = self.pickerHeaderView.frame;
CGFloat titleBarHeight = self.pickerStyle.hiddenTitleBarView ? 0 : self.pickerStyle.titleBarHeight;
self.pickerHeaderView.frame = CGRectMake(0, titleBarHeight, self.alertView.bounds.size.width, rect.size.height);
self.pickerHeaderView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.alertView addSubview:self.pickerHeaderView];
}
if (self.pickerFooterView) {
CGRect rect = self.pickerFooterView.frame;
self.pickerFooterView.frame = CGRectMake(0, self.alertView.bounds.size.height - self.pickerStyle.paddingBottom - rect.size.height, self.alertView.bounds.size.width, rect.size.height);
self.pickerFooterView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.alertView addSubview:self.pickerFooterView];
}
[self.keyView addSubview:self];
// iOS16 alertView frame
CGFloat accessoryViewHeight = 0;
if (self.pickerHeaderView) {
accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
}
if (self.pickerFooterView) {
accessoryViewHeight += self.pickerFooterView.bounds.size.height;
}
CGFloat height = self.pickerStyle.titleBarHeight + self.pickerStyle.pickerHeight + self.pickerStyle.paddingBottom + accessoryViewHeight;
self.alertView.frame = CGRectMake(0, self.keyView.bounds.size.height - height, self.keyView.bounds.size.width, height);
//
CGRect rect = self.alertView.frame;
rect.origin.y = self.bounds.size.height;
self.alertView.frame = rect;
//
if (!self.pickerStyle.hiddenMaskView) {
self.maskBgView.alpha = 0;
}
[UIView animateWithDuration:0.3f animations:^{
if (!self.pickerStyle.hiddenMaskView) {
self.maskBgView.alpha = 1;
}
CGFloat alertViewHeight = self.alertView.bounds.size.height;
CGRect rect = self.alertView.frame;
rect.origin.y -= alertViewHeight;
self.alertView.frame = rect;
}];
}
}
#pragma mark -
- (void)removePickerFromView:(UIView *)view {
if (view) {
[self removeFromSuperview];
} else {
//
[UIView animateWithDuration:0.2f animations:^{
CGFloat alertViewHeight = self.alertView.bounds.size.height;
CGRect rect = self.alertView.frame;
rect.origin.y += alertViewHeight;
self.alertView.frame = rect;
if (!self.pickerStyle.hiddenMaskView) {
self.maskBgView.alpha = 0;
}
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
}
#pragma mark -
- (void)reloadData {
}
#pragma mark - picker
- (void)addSubViewToPicker:(UIView *)customView {
}
#pragma mark - titleBar
- (void)addSubViewToTitleBar:(UIView *)customView {
if (!self.pickerStyle.hiddenTitleBarView) {
[self.titleBarView addSubview:customView];
}
}
- (BRPickerStyle *)pickerStyle {
if (!_pickerStyle) {
_pickerStyle = [[BRPickerStyle alloc]init];
}
return _pickerStyle;
}
- (UIView *)keyView {
if (!_keyView) {
_keyView = BRGetKeyWindow();
}
return _keyView;
}
#pragma mark - setter
- (void)setTitle:(NSString *)title {
_title = title;
if (_titleLabel) {
_titleLabel.text = title;
}
}
- (void)dealloc {
NSLog(@"%@ dealloc", NSStringFromClass([self class]));
}
@end

View File

@ -0,0 +1,273 @@
//
// BRPickerStyle.h
// BRPickerViewDemo
//
// Created by renbo on 2019/10/2.
// Copyright © 2019 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "BRPickerViewMacro.h"
NS_ASSUME_NONNULL_BEGIN
// 边框样式(左边取消按钮/右边确定按钮)
typedef NS_ENUM(NSInteger, BRBorderStyle) {
/** 无边框(默认) */
BRBorderStyleNone = 0,
/** 有圆角和边框 */
BRBorderStyleSolid,
/** 仅有圆角 */
BRBorderStyleFill
};
@interface BRPickerStyle : NSObject
/////////////////////////////// 蒙层视图maskView///////////////////////////////
/** 设置背景颜色 */
@property (nullable, nonatomic, strong) UIColor *maskColor;
/** 隐藏 maskView默认为 NO */
@property (nonatomic, assign) BOOL hiddenMaskView;
////////////////////////////// 弹框视图alertView///////////////////////////////
/** 设置 alertView 弹框视图的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *alertViewColor;
/** 设置 alertView 弹框视图左上和右上的圆角半径 */
@property (nonatomic, assign) NSInteger topCornerRadius;
/** 设置 alertView 弹框视图顶部边框线颜色 */
@property (nullable, nonatomic, strong) UIColor *shadowLineColor;
/** 设置 alertView 弹框视图顶部边框线高度 */
@property (nonatomic, assign) CGFloat shadowLineHeight;
/** 隐藏 alertView 弹框视图顶部边框线,默认为 NO */
@property (nonatomic, assign) BOOL hiddenShadowLine;
/** 设置 alertView 弹框视图底部内边距,默认为安全区域底部距屏幕底部的高度 */
@property (nonatomic, assign) CGFloat paddingBottom;
//////////////////////////// 标题栏视图titleBarView ////////////////////////////
/** 设置 titleBarView 标题栏的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *titleBarColor;
/** 设置 titleBarView 标题栏的高度 */
@property (nonatomic, assign) CGFloat titleBarHeight;
/** 设置 titleBarView 标题栏底部分割线颜色 */
@property (nullable, nonatomic, strong) UIColor *titleLineColor;
/** 设置 titleBarView 标题栏底部分割线左边间距 */
@property (nonatomic, assign) CGFloat titleLineLeftSpace;
/** 设置 titleBarView 标题栏底部分割线左边间距 */
@property (nonatomic, assign) CGFloat titleLineRightSpace;
/** 设置 titleBarView 标题栏底部分割线高度 */
@property (nonatomic, assign) CGFloat titleLineHeight;
/** 隐藏 titleBarView 标题栏底部分割线,默认为 NO */
@property (nonatomic, assign) BOOL hiddenTitleLine;
/** 隐藏 titleBarView默认为 NO */
@property (nonatomic, assign) BOOL hiddenTitleBarView;
////////////////////////// 标题栏中间labeltitleLabel///////////////////////////
/** 设置 titleLabel 的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *titleLabelColor;
/** 设置 titleLabel 文本颜色 */
@property (nullable, nonatomic, strong) UIColor *titleTextColor;
/** 设置 titleLabel 字体大小 */
@property (nullable, nonatomic, strong) UIFont *titleTextFont;
/** 设置 titleLabel 的 frame */
@property (nonatomic, assign) CGRect titleLabelFrame;
/** 隐藏 titleLabel默认为 NO */
@property (nonatomic, assign) BOOL hiddenTitleLabel;
/////////////////////////////// 取消按钮cancelBtn//////////////////////////////
/** 设置 cancelBtn 的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *cancelColor;
/** 设置 cancelBtn 标题的颜色 */
@property (nullable, nonatomic, strong) UIColor *cancelTextColor;
/** 设置 cancelBtn 标题的字体 */
@property (nullable, nonatomic, strong) UIFont *cancelTextFont;
/** 设置 cancelBtn 的 frame */
@property (nonatomic, assign) CGRect cancelBtnFrame;
/** 设置 cancelBtn 的边框样式 */
@property (nonatomic, assign) BRBorderStyle cancelBorderStyle;
/** 设置 cancelBtn 的圆角大小 */
@property (nonatomic, assign) CGFloat cancelCornerRadius;
/** 设置 cancelBtn 的边框宽度 */
@property (nonatomic, assign) CGFloat cancelBorderWidth;
/** 设置 cancelBtn 的 image */
@property (nullable, nonatomic, strong) UIImage *cancelBtnImage;
/** 设置 cancelBtn 的 title */
@property (nullable, nonatomic, copy) NSString *cancelBtnTitle;
/** 隐藏 cancelBtn默认为 NO */
@property (nonatomic, assign) BOOL hiddenCancelBtn;
/////////////////////////////// 确定按钮doneBtn////////////////////////////////
/** 设置 doneBtn 的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *doneColor;
/** 设置 doneBtn 标题的颜色 */
@property (nullable, nonatomic, strong) UIColor *doneTextColor;
/** 设置 doneBtn 标题的字体 */
@property (nullable, nonatomic, strong) UIFont *doneTextFont;
/** 设置 doneBtn 的 frame */
@property (nonatomic, assign) CGRect doneBtnFrame;
/** 设置 doneBtn 的边框样式 */
@property (nonatomic, assign) BRBorderStyle doneBorderStyle;
/** 设置 doneBtn 的圆角大小 */
@property (nonatomic, assign) CGFloat doneCornerRadius;
/** 设置 doneBtn 的边框宽度 */
@property (nonatomic, assign) CGFloat doneBorderWidth;
/** 设置 doneBtn 的 image */
@property (nullable, nonatomic, strong) UIImage *doneBtnImage;
/** 设置 doneBtn 的 title */
@property (nullable, nonatomic, copy) NSString *doneBtnTitle;
/** 隐藏 doneBtn默认为 NO */
@property (nonatomic, assign) BOOL hiddenDoneBtn;
/////////////////////////////// 选择器pickerView///////////////////////////////
/** 设置 picker 的背景颜色 */
@property (nullable, nonatomic, strong) UIColor *pickerColor;
/** 设置 picker 中间两条分割线的背景颜色。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIColor *separatorColor;
/** 设置 picker 中间两条分割线的高度。暂不支持日期选择器前4种类型 */
@property (nonatomic, assign) CGFloat separatorHeight;
/** 设置 picker 中间两条分割线的左侧间距。暂不支持日期选择器前4种类型 */
@property (nonatomic, assign) CGFloat separatorLeftSpace;
/** 设置 picker 中间两条分割线的右侧间距。暂不支持日期选择器前4种类型 */
@property (nonatomic, assign) CGFloat separatorRightSpace;
/** 设置 picker 文本的颜色。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIColor *pickerTextColor;
/** 设置 picker 文本的字体。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIFont *pickerTextFont;
/** 设置 picker 中间选中行的背景颜色。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIColor *selectRowColor;
/** 设置 picker 中间选中行文本的颜色。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIColor *selectRowTextColor;
/** 设置 picker 中间选中行文本的字体。暂不支持日期选择器前4种类型 */
@property (nullable, nonatomic, strong) UIFont *selectRowTextFont;
/** 设置 picker 的高度,系统默认高度为 216 */
@property (nonatomic, assign) CGFloat pickerHeight;
/** 设置 picker 的行高,默认为 35 */
@property (nonatomic, assign) CGFloat rowHeight;
/** 设置 picker 的列宽 */
@property (nonatomic, assign) CGFloat columnWidth;
/** 设置 picker 的列间隔,仅支持`BRTextPickerView` */
@property (nonatomic, assign) CGFloat columnSpacing;
/** 设置 picker 文本支持的最大行数,默认为 2 */
@property (nonatomic, assign) NSUInteger maxTextLines;
/**
* iOS14之后选择器默认自带的新样式4
* 线iOS14之前的样式保持一致 YES
*/
@property (nonatomic, assign) BOOL clearPickerNewStyle;
/**
* nil时
* language: zh-Hanszh-Hanten
*/
@property(nullable, nonatomic, copy) NSString *language;
/////// 日期选择器单位样式showUnitType == BRShowUnitTypeOnlyCenter 时生效。暂不支持日期选择器前4种类型 ///////
/** 设置日期选择器单位文本的颜色 */
@property (nullable, nonatomic, strong) UIColor *dateUnitTextColor;
/** 设置日期选择器单位文本的字体 */
@property (nullable, nonatomic, strong) UIFont *dateUnitTextFont;
/** 设置日期选择器单位 label 的水平方向偏移量 */
@property (nonatomic, assign) CGFloat dateUnitOffsetX;
/** 设置日期选择器单位 label 的竖直方向偏移量 */
@property (nonatomic, assign) CGFloat dateUnitOffsetY;
//////////////////////////////// 常用的几种模板样式 ////////////////////////////////
/// 弹框模板样式1 - 取消/确定按钮圆角样式
/// @param themeColor 主题颜色
+ (instancetype)pickerStyleWithThemeColor:(nullable UIColor *)themeColor;
/// 弹框模板样式2 - 顶部圆角样式 + 完成按钮
/// @param doneTextColor 完成按钮标题的颜色
+ (instancetype)pickerStyleWithDoneTextColor:(nullable UIColor *)doneTextColor;
/// 弹框模板样式3 - 顶部圆角样式 + 图标按钮
/// @param doneBtnImage 完成按钮的 image
+ (instancetype)pickerStyleWithDoneBtnImage:(nullable UIImage *)doneBtnImage;
//////////////////////////////// 以下是组件内部使用的几个封装方法 ////////////////////////////////
/** 设置选择器中间选中行的样式 */
- (void)setupPickerSelectRowStyle:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
/** 添加选择器中间行上下两条分割线iOS14之后系统默认去掉需要手动添加*/
- (void)addSeparatorLineView:(UIView *)pickerView;
/** 设置 view 的部分圆角 */
// corners(枚举类型,可组合使用)UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners
+ (void)br_setView:(UIView *)view roundingCorners:(UIRectCorner)corners withRadius:(CGFloat)radius;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,508 @@
//
// BRPickerStyle.m
// BRPickerViewDemo
//
// Created by renbo on 2019/10/2.
// Copyright © 2019 irenb. All dones reserved.
//
// https://github.com/agiapp/BRPickerView
#import "BRPickerStyle.h"
#import "NSBundle+BRPickerView.h"
//
#define kBRDefaultTextColor BR_RGB_HEX(0x333333, 1.0f)
@implementation BRPickerStyle
- (instancetype)init {
if (self = [super init]) {
self.clearPickerNewStyle = YES;
}
return self;
}
///
- (UIColor *)maskColor {
if (!_maskColor) {
_maskColor = [self br_colorWithLightColor:BR_RGB_HEX(0x000000, 0.3f) darkColor:BR_RGB_HEX(0x666666, 0.3f)];
}
return _maskColor;
}
- (UIColor *)shadowLineColor {
if (!_shadowLineColor) {
if (@available(iOS 13.0, *)) {
// 线
_shadowLineColor = [UIColor separatorColor];
} else {
_shadowLineColor = BR_RGB_HEX(0xc6c6c8, 1.0f);
}
}
return _shadowLineColor;
}
- (CGFloat)shadowLineHeight {
if (_shadowLineHeight <= 0 || _shadowLineHeight > 5.0f) {
_shadowLineHeight = 0.5f;
}
return _shadowLineHeight;
}
- (CGFloat)paddingBottom {
if (_paddingBottom <= 0) {
_paddingBottom = BR_BOTTOM_MARGIN;
}
return _paddingBottom;
}
- (UIColor *)titleBarColor {
if (!_titleBarColor) {
if (@available(iOS 13.0, *)) {
// #ffffff()#1c1c1e()
_titleBarColor = [UIColor secondarySystemGroupedBackgroundColor];
} else {
_titleBarColor = [UIColor whiteColor];
}
}
return _titleBarColor;
}
- (CGFloat)titleBarHeight {
if (!self.hiddenTitleBarView) {
if (_titleBarHeight < 44.0f && (!self.hiddenCancelBtn || !self.hiddenDoneBtn || !self.hiddenTitleLabel)) {
_titleBarHeight = 44.0f;
}
} else {
_titleBarHeight = 0;
}
return _titleBarHeight;
}
- (UIColor *)titleLineColor {
if (!_titleLineColor) {
_titleLineColor = [self br_colorWithLightColor:BR_RGB_HEX(0xededee, 1.0f) darkColor:BR_RGB_HEX(0x18181c, 1.0f)];
}
return _titleLineColor;
}
- (CGFloat)titleLineHeight {
if (_titleLineHeight <= 0) {
return 0.5f;
}
return _titleLineHeight;
}
- (UIColor *)cancelColor {
if (!_cancelColor) {
_cancelColor = [UIColor clearColor];
}
return _cancelColor;
}
- (UIColor *)cancelTextColor {
if (!_cancelTextColor) {
if (@available(iOS 13.0, *)) {
_cancelTextColor = [UIColor labelColor];
} else {
_cancelTextColor = kBRDefaultTextColor;
}
}
return _cancelTextColor;
}
- (UIFont *)cancelTextFont {
if (!_cancelTextFont) {
_cancelTextFont = [UIFont systemFontOfSize:16.0f];
}
return _cancelTextFont;
}
- (NSString *)cancelBtnTitle {
if (!_cancelBtnTitle && !_cancelBtnImage) {
_cancelBtnTitle = [NSBundle br_localizedStringForKey:@"取消" language:self.language];
}
return _cancelBtnTitle;
}
- (CGRect)cancelBtnFrame {
if (CGRectEqualToRect(_cancelBtnFrame, CGRectZero) || _cancelBtnFrame.size.height == 0) {
_cancelBtnFrame = CGRectMake(5, 8, 60, 28);
}
return _cancelBtnFrame;
}
- (UIColor *)titleLabelColor {
if (!_titleLabelColor) {
_titleLabelColor = [UIColor clearColor];
}
return _titleLabelColor;
}
- (UIColor *)titleTextColor {
if (!_titleTextColor) {
if (@available(iOS 13.0, *)) {
_titleTextColor = [UIColor secondaryLabelColor];
} else {
_titleTextColor = BR_RGB_HEX(0x999999, 1.0f);
}
}
return _titleTextColor;
}
- (UIFont *)titleTextFont {
if (!_titleTextFont) {
_titleTextFont = [UIFont systemFontOfSize:15.0f];
}
return _titleTextFont;
}
- (CGRect)titleLabelFrame {
if (CGRectEqualToRect(_titleLabelFrame, CGRectZero) || _titleLabelFrame.size.height == 0) {
_titleLabelFrame = CGRectMake(5 + 60 + 2, 0, BRGetKeyWindow().bounds.size.width - 2 * (5 + 60 + 2), 44);
}
return _titleLabelFrame;
}
- (UIColor *)doneColor {
if (!_doneColor) {
_doneColor = [UIColor clearColor];
}
return _doneColor;
}
- (UIColor *)doneTextColor {
if (!_doneTextColor) {
if (@available(iOS 13.0, *)) {
_doneTextColor = [UIColor labelColor];
} else {
_doneTextColor = kBRDefaultTextColor;
}
}
return _doneTextColor;
}
- (UIFont *)doneTextFont {
if (!_doneTextFont) {
_doneTextFont = [UIFont systemFontOfSize:16.0f];
}
return _doneTextFont;
}
- (NSString *)doneBtnTitle {
if (!_doneBtnTitle && !_doneBtnImage) {
_doneBtnTitle = [NSBundle br_localizedStringForKey:@"确定" language:self.language];
}
return _doneBtnTitle;
}
- (CGRect)doneBtnFrame {
if (CGRectEqualToRect(_doneBtnFrame, CGRectZero) || _doneBtnFrame.size.height == 0) {
_doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 60 - 5, 8, 60, 28);
}
return _doneBtnFrame;
}
- (UIColor *)pickerColor {
if (!_pickerColor) {
if (@available(iOS 13.0, *)) {
// #ffffff()#1c1c1e()
_pickerColor = [UIColor secondarySystemGroupedBackgroundColor];
} else {
_pickerColor = [UIColor whiteColor];
}
}
return _pickerColor;
}
- (UIColor *)separatorColor {
if (!_separatorColor) {
if (@available(iOS 13.0, *)) {
// 线
_separatorColor = [UIColor opaqueSeparatorColor];
} else {
_separatorColor = BR_RGB_HEX(0xc6c6c8, 1.0f);
}
}
return _separatorColor;
}
- (UIColor *)pickerTextColor {
if (!_pickerTextColor) {
if (@available(iOS 13.0, *)) {
_pickerTextColor = [UIColor labelColor];
} else {
_pickerTextColor = kBRDefaultTextColor;
}
}
return _pickerTextColor;
}
- (UIFont *)pickerTextFont {
if (!_pickerTextFont) {
_pickerTextFont = [UIFont systemFontOfSize:18.0f];
}
return _pickerTextFont;
}
- (CGFloat)pickerHeight {
if (_pickerHeight < 40) {
_pickerHeight = 216.0f;
}
return _pickerHeight;
}
- (CGFloat)rowHeight {
if (_rowHeight < 20) {
_rowHeight = 35.0f;
}
return _rowHeight;
}
- (NSUInteger)maxTextLines {
if (_maxTextLines < 1) {
_maxTextLines = 2;
}
return _maxTextLines;
}
- (NSString *)language {
if (!_language) {
//
// zh-Hans-CN()zh-Hant-CN()en-CN()en-GB()
// `CN`iOS9CN US
_language = [NSLocale preferredLanguages].firstObject;
}
return _language;
}
- (UIColor *)dateUnitTextColor {
if (!_dateUnitTextColor) {
if (@available(iOS 13.0, *)) {
_dateUnitTextColor = [UIColor labelColor];
} else {
_dateUnitTextColor = kBRDefaultTextColor;
}
}
return _dateUnitTextColor;
}
- (UIFont *)dateUnitTextFont {
if (!_dateUnitTextFont) {
_dateUnitTextFont = [UIFont systemFontOfSize:18.0f];
}
return _dateUnitTextFont;
}
#pragma mark -
- (UIColor *)br_colorWithLightColor:(UIColor *)lightColor darkColor:(UIColor *)darkColor {
if (@available(iOS 13.0, *)) {
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
return lightColor;
} else {
return darkColor;
}
}];
return dyColor;
} else {
return lightColor;
}
}
#pragma mark - 1 - /
+ (instancetype)pickerStyleWithThemeColor:(UIColor *)themeColor {
BRPickerStyle *customStyle = [[self alloc]init];
if (themeColor) {
customStyle.cancelTextColor = themeColor;
customStyle.cancelBorderStyle = BRBorderStyleSolid;
customStyle.doneColor = themeColor;
customStyle.doneTextColor = [UIColor whiteColor];
customStyle.doneBorderStyle = BRBorderStyleFill;
}
return customStyle;
}
#pragma mark - 2 - +
+ (instancetype)pickerStyleWithDoneTextColor:(UIColor *)doneTextColor {
BRPickerStyle *customStyle = [[self alloc]init];
if (doneTextColor) {
customStyle.topCornerRadius = 16.0f;
customStyle.hiddenCancelBtn = YES;
customStyle.hiddenTitleLine = YES;
customStyle.titleLabelFrame = CGRectMake(20, 4, 100, 40);
customStyle.doneTextColor = doneTextColor;
customStyle.doneTextFont = [UIFont boldSystemFontOfSize:18.0f];
customStyle.doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 60, 4, 60, 40);
customStyle.doneBtnTitle = [NSBundle br_localizedStringForKey:@"完成" language:customStyle.language];
customStyle.selectRowTextColor = doneTextColor;
customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
}
return customStyle;
}
#pragma mark - 3 - +
+ (instancetype)pickerStyleWithDoneBtnImage:(UIImage *)doneBtnImage {
BRPickerStyle *customStyle = [[self alloc]init];
if (doneBtnImage) {
customStyle.topCornerRadius = 16.0f;
customStyle.hiddenTitleLine = YES;
customStyle.hiddenCancelBtn = YES;
customStyle.titleLabelFrame = CGRectMake(20, 4, 100, 40);
customStyle.doneBtnImage = doneBtnImage;
customStyle.doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 44, 4, 40, 40);
}
return customStyle;
}
#pragma mark -
- (void)setupPickerSelectRowStyle:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
// 1.线
NSString *systemVersion = [UIDevice currentDevice].systemVersion;
if (systemVersion.doubleValue < 14.0) {
for (UIView *subView in pickerView.subviews) {
if (subView && [subView isKindOfClass:[UIView class]] && subView.frame.size.height <= 1) {
subView.backgroundColor = self.separatorColor;
// 线
if (self.separatorHeight > 0) {
CGRect rect = subView.frame;
rect.size.height = self.separatorHeight;
subView.frame = rect;
}
}
}
}
// 2.
UIView *contentView = nil;
NSArray *subviews = pickerView.subviews;
if (subviews.count > 0) {
id firstView = subviews.firstObject;
if (firstView && [firstView isKindOfClass:[UIView class]]) {
contentView = (UIView *)firstView;
}
}
if (self.selectRowColor) {
UIView *columnView = nil;
if (contentView) {
id obj = [contentView valueForKey:@"subviewCache"];
if (obj && [obj isKindOfClass:[NSArray class]]) {
NSArray *columnViews = (NSArray *)obj;
if (columnViews.count > 0) {
id columnObj = columnViews.firstObject;
if (columnObj && [columnObj isKindOfClass:[UIView class]]) {
columnView = (UIView *)columnObj;
}
}
}
}
if (columnView) {
id obj = [columnView valueForKey:@"middleContainerView"];
if (obj && [obj isKindOfClass:[UIView class]]) {
UIView *selectRowView = (UIView *)obj;
//
selectRowView.backgroundColor = self.selectRowColor;
}
}
}
if (contentView && self.clearPickerNewStyle) {
if (systemVersion.doubleValue >= 14.0) {
//
id lastView = subviews.lastObject;
if (lastView && [lastView isKindOfClass:[UIView class]]) {
UIView *rectBgView = (UIView *)lastView;
rectBgView.hidden = YES;
}
// iOS14
if (systemVersion.doubleValue < 15.0f) {
[self setPickerAllSubViewsStyle:contentView];
}
}
}
// 3./
if (self.selectRowTextColor || self.selectRowTextFont) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// label
UILabel *selectLabel = (UILabel *)[pickerView viewForRow:row forComponent:component];
if (selectLabel) {
if (self.selectRowTextColor) {
selectLabel.textColor = self.selectRowTextColor;
}
if (self.selectRowTextFont) {
selectLabel.font = self.selectRowTextFont;
}
}
});
}
}
// frame iOS14 UIPickerView
- (void)setPickerAllSubViewsStyle:(UIView *)view {
NSArray *subViews = view.subviews;
if (subViews.count == 0 || [view isKindOfClass:[UILabel class]]) return;
for (UIView *subView in subViews) {
NSString *className = NSStringFromClass([subView class]);
if ([className isEqualToString:@"UIPickerColumnView"]) {
CGRect rect = subView.frame;
rect.origin.x = 0;
rect.size.width = view.bounds.size.width;
subView.frame = rect;
}
NSString *superClassName = NSStringFromClass([view class]);
if ([superClassName isEqualToString:@"UIPickerColumnView"]) {
CGRect rect = subView.frame;
rect.size.width = view.bounds.size.width;
subView.frame = rect;
}
if ([subView isKindOfClass:[UILabel class]]) {
CGRect rect = subView.frame;
rect.origin.x = 10;
subView.frame = rect;
}
[self setPickerAllSubViewsStyle:subView];
}
}
#pragma mark - 线iOS14
- (void)addSeparatorLineView:(UIView *)pickerView {
if ([UIDevice currentDevice].systemVersion.doubleValue >= 14.0) {
UIView *topLineView = [[UIView alloc]initWithFrame:CGRectMake(self.separatorLeftSpace, pickerView.bounds.size.height / 2 - self.rowHeight / 2, pickerView.bounds.size.width - self.separatorLeftSpace - self.separatorRightSpace, 0.5f)];
topLineView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
topLineView.backgroundColor = self.separatorColor;
// 线
if (self.separatorHeight > 0) {
CGRect topRect = topLineView.frame;
topRect.size.height = self.separatorHeight;
topLineView.frame = topRect;
}
[pickerView addSubview:topLineView];
UIView *bottomLineView = [[UIView alloc]initWithFrame:CGRectMake(self.separatorLeftSpace, pickerView.bounds.size.height / 2 + self.rowHeight / 2, pickerView.bounds.size.width - self.separatorLeftSpace - self.separatorRightSpace, 0.5f)];
bottomLineView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
bottomLineView.backgroundColor = self.separatorColor;
// 线
if (self.separatorHeight > 0) {
CGRect bottomRect = bottomLineView.frame;
bottomRect.size.height = self.separatorHeight;
bottomLineView.frame = bottomRect;
}
[pickerView addSubview:bottomLineView];
}
}
#pragma mark - view
// corners(使)UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners
+ (void)br_setView:(UIView *)view roundingCorners:(UIRectCorner)corners withRadius:(CGFloat)radius {
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *shape = [[CAShapeLayer alloc]init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
@end

View File

@ -0,0 +1,35 @@
/*
Localizable.strings
BRPickerViewDemo
Created by renbo on 2019/10/30.
Copyright © 2019 irenb. All rights reserved.
*/
"确定" = "OK";
"取消" = "Cancel";
"完成" = "Done";
"年" = " ";
"月" = " ";
"日" = " ";
"时" = " ";
"分" = " ";
"秒" = " ";
"周" = " ";
"季度" = " ";
"上午" = "AM";
"下午" = "PM";
"至今" = " Now";
"今天" = " Today";
"周一" = " Mon";
"周二" = " Tue";
"周三" = " Wed";
"周四" = " Thu";
"周五" = " Fri";
"周六" = " Sat";
"周日" = " Sun";

View File

@ -0,0 +1,35 @@
/*
Localizable.strings
BRPickerViewDemo
Created by renbo on 2019/10/30.
Copyright © 2019 irenb. All rights reserved.
*/
"确定" = "确定";
"取消" = "取消";
"完成" = "完成";
"年" = "年";
"月" = "月";
"日" = "日";
"时" = "时";
"分" = "分";
"秒" = "秒";
"周" = "周";
"季度" = "季度";
"上午" = "上午";
"下午" = "下午";
"至今" = "至今";
"今天" = "今天";
"周一" = "周一";
"周二" = "周二";
"周三" = "周三";
"周四" = "周四";
"周五" = "周五";
"周六" = "周六";
"周日" = "周日";

View File

@ -0,0 +1,35 @@
/*
Localizable.strings
BRPickerViewDemo
Created by renbo on 2019/10/30.
Copyright © 2019 irenb. All rights reserved.
*/
"确定" = "確定";
"取消" = "取消";
"完成" = "完成";
"年" = "年";
"月" = "月";
"日" = "日";
"时" = "時";
"分" = "分";
"秒" = "秒";
"周" = "周";
"季度" = "季度";
"上午" = "上午";
"下午" = "下午";
"至今" = "至今";
"今天" = "今天";
"周一" = "周壹";
"周二" = "周二";
"周三" = "周三";
"周四" = "周四";
"周五" = "周五";
"周六" = "周六";
"周日" = "周日";

View File

@ -0,0 +1,85 @@
//
// BRPickerViewMacro.h
// BRPickerViewDemo
//
// Created by renbo on 2018/4/23.
// Copyright © 2018 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#ifndef BRPickerViewMacro_h
#define BRPickerViewMacro_h
#import <UIKit/UIKit.h>
;
// 屏幕安全区域下边距
#define BR_BOTTOM_MARGIN \
({CGFloat safeBottomHeight = 0;\
if (@available(iOS 11.0, *)) {\
safeBottomHeight = BRGetKeyWindow().safeAreaInsets.bottom;\
}\
(safeBottomHeight);})
// 静态库中编写 Category 时的便利宏,用于解决 Category 方法从静态库中加载需要特别设置的问题
#ifndef BRSYNTH_DUMMY_CLASS
#define BRSYNTH_DUMMY_CLASS(_name_) \
@interface BRSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation BRSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif
// 打印错误日志
#ifdef DEBUG
#define BRErrorLog(...) NSLog(@"【BRPickerView】error log. reason: %@", [NSString stringWithFormat:__VA_ARGS__])
#else
#define BRErrorLog(...)
#endif
/** RGB颜色(16进制) */
static inline UIColor *BR_RGB_HEX(uint32_t rgbValue, CGFloat alpha) {
return [UIColor colorWithRed:((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255.0
green:((CGFloat)((rgbValue & 0xFF00) >> 8)) / 255.0
blue:((CGFloat)(rgbValue & 0xFF)) / 255.0
alpha:(alpha)];
}
/** 获取 keyWindow */
static inline UIWindow *BRGetKeyWindow(void) {
UIWindow *keyWindow = nil;
if (@available(iOS 13.0, *)) { // 运行时检查系统版本(兼容不同版本的系统,防止运行报错)
NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
for (UIScene *scene in connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
UIWindowScene *windowScene = (UIWindowScene *)scene;
for (UIWindow *window in windowScene.windows) {
if (window.isKeyWindow) {
keyWindow = window;
break;
}
}
}
}
}
if (!keyWindow) {
keyWindow = [UIApplication sharedApplication].windows.firstObject;
if (!keyWindow.isKeyWindow) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (CGRectEqualToRect(window.bounds, UIScreen.mainScreen.bounds)) {
keyWindow = window;
}
#endif
}
}
return keyWindow;
}
#endif /* BRPickerViewMacro_h */

View File

@ -0,0 +1,27 @@
//
// NSBundle+BRPickerView.h
// BRPickerViewDemo
//
// Created by renbo on 2019/10/30.
// Copyright © 2019 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSBundle (BRPickerView)
/// 获取 BRPickerView 的资源包单例
+ (instancetype)br_pickerBundle;
/// 获取本地化字符串(简化版本)
/// @param key 本地化字符串的键(即 Localizable.strings 文件中 key-value 中的 key
/// @param language 目标语言代码,如果为 nil 则使用系统首选语言
/// @return 返回对应语言的本地化字符串,如果找不到则返回空字符串
+ (NSString *)br_localizedStringForKey:(NSString *)key language:(nullable NSString *)language;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,191 @@
//
// NSBundle+BRPickerView.m
// BRPickerViewDemo
//
// Created by renbo on 2019/10/30.
// Copyright © 2019 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "NSBundle+BRPickerView.h"
#import "BRPickerAlertView.h"
BRSYNTH_DUMMY_CLASS(NSBundle_BRPickerView)
@implementation NSBundle (BRPickerView)
#pragma mark - BRPickerView.bundle
/// BRPickerView
/// @discussion 使 dispatch_once 线 BRPickerView.bundle
/// @return BRPickerView.bundle NSBundle bundle
+ (instancetype)br_pickerBundle {
static NSBundle *pickerBundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 使 BRPickerAlertView bundle
//
// 1.CocoaPods Pods/BRPickerView/
// 2.SPM .build/checkouts/BRPickerView/
// 3.
#ifdef SWIFT_PACKAGE
// 1: Swift Package Manager
NSBundle *bundle = SWIFTPM_MODULE_BUNDLE; // SPM SPM
#else
// 2: CocoaPods
NSBundle *bundle = [NSBundle bundleForClass:[BRPickerAlertView class]];
#endif
// bundle BRPickerView.bundle
NSString *bundlePath = [bundle pathForResource:@"BRPickerView" ofType:@"bundle"];
if (bundlePath) {
pickerBundle = [NSBundle bundleWithPath:bundlePath];
}
// BRPickerView.bundle退 bundle
if (!pickerBundle) {
pickerBundle = bundle;
NSLog(@"⚠️ BRPickerView.bundle not found, using main bundle instead");
}
});
return pickerBundle;
}
#pragma mark -
///
/// @param key Localizable.strings key-value key
/// @param language nil 使
/// @return
+ (NSString *)br_localizedStringForKey:(NSString *)key language:(NSString *)language {
return [self br_localizedStringForKey:key value:nil language:language];
}
///
/// @param key Localizable.strings key-value key
/// @param value
/// @param language nil 使
/// @return
+ (NSString *)br_localizedStringForKey:(NSString *)key value:(NSString *)value language:(NSString *)language {
//
if (!key || key.length == 0) return @"";
// 使
NSString *preferredLanguage = language ?: [NSLocale preferredLanguages].firstObject;
NSString *standardizedLanguage = [self standardizeLanguageCode:preferredLanguage];
// bundle
NSBundle *languageBundle = [self bundleForLanguage:standardizedLanguage];
//
NSString *localizedString = [languageBundle localizedStringForKey:key value:value table:nil];
// 退 bundle
BOOL needsFallback = !localizedString ||
[localizedString isEqualToString:value] ||
localizedString.length == 0;
if (needsFallback) {
// 退 bundle
localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
}
// BRPickerView value
if (!localizedString || [localizedString isEqualToString:value] || localizedString.length == 0) {
// 退
localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
}
return localizedString ?: (value ?: @"");
}
#pragma mark -
///
/// @discussion
/// @param languageCode
/// @return
+ (NSString *)standardizeLanguageCode:(NSString *)languageCode {
if (!languageCode || languageCode.length == 0) {
return @"en"; //
}
NSString *lowercaseCode = [languageCode lowercaseString]; //
NSString *uppercaseCode = [languageCode uppercaseString]; //
//
if ([lowercaseCode hasPrefix:@"en"]) {
return @"en";
}
//
if ([lowercaseCode hasPrefix:@"zh"]) {
//
if ([uppercaseCode containsString:@"HANS"] ||
[uppercaseCode containsString:@"CN"] ||
[languageCode isEqualToString:@"zh_CN"] ||
[lowercaseCode isEqualToString:@"zh"] ||
[uppercaseCode containsString:@"SIMPLIFIED"]) {
return @"zh-Hans"; //
}
//
if ([uppercaseCode containsString:@"HANT"] ||
[uppercaseCode containsString:@"TW"] ||
[uppercaseCode containsString:@"HK"] ||
[uppercaseCode containsString:@"MO"] ||
[uppercaseCode containsString:@"TRADITIONAL"]) {
return @"zh-Hant"; //
}
//
return @"zh-Hans";
}
//
//
return @"en"; // 退
}
#pragma mark -
/// bundle
/// @param language
/// @return lproj bundle bundle
+ (NSBundle *)bundleForLanguage:(NSString *)language {
if (!language || language.length == 0) {
language = @"en"; //
}
NSBundle *pickerBundle = [self br_pickerBundle];
// 1.
NSString *path = [pickerBundle pathForResource:language ofType:@"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
if (languageBundle) {
return languageBundle;
}
// 2. 退
NSString *fallbackLanguage = nil;
if ([language isEqualToString:@"zh-Hant"]) {
// 退
fallbackLanguage = @"zh-Hans";
} else if ([language hasPrefix:@"zh"]) {
// 退
fallbackLanguage = @"zh-Hans";
} else {
// 退
fallbackLanguage = @"en";
}
if (fallbackLanguage) {
path = [pickerBundle pathForResource:fallbackLanguage ofType:@"lproj"];
languageBundle = [NSBundle bundleWithPath:path];
if (languageBundle) {
return languageBundle;
}
}
// 3. 退 picker bundle
return pickerBundle;
}
@end

View File

@ -0,0 +1,111 @@
//
// BRDatePickerView+BR.h
// BRPickerViewDemo
//
// Created by renbo on 2020/6/16.
// Copyright © 2020 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import "BRDatePickerView.h"
NS_ASSUME_NONNULL_BEGIN
@interface BRDatePickerView (BR)
/** 最小日期 */
- (NSDate *)handlerMinDate:(nullable NSDate *)minDate;
/** 最大日期 */
- (NSDate *)handlerMaxDate:(nullable NSDate *)maxDate;
/** 默认选中的日期 */
- (NSDate *)handlerSelectDate:(nullable NSDate *)selectDate dateFormat:(NSString *)dateFormat;
/** NSDate 转 NSString */
- (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat;
/** NSString 转 NSDate */
- (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat;
/** 比较两个日期大小(可以指定比较级数,即按指定格式进行比较) */
- (NSComparisonResult)br_compareDate:(NSDate *)date targetDate:(NSDate *)targetDate dateFormat:(NSString *)dateFormat;
/** 获取 yearArr 数组 */
- (NSArray *)getYearArr;
/** 获取 monthArr 数组 */
- (NSArray *)getMonthArr:(NSInteger)year;
/** 获取 dayArr 数组 */
- (NSArray *)getDayArr:(NSInteger)year month:(NSInteger)month;
/** 获取 hourArr 数组 */
- (NSArray *)getHourArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
/** 获取 minuteArr 数组 */
- (NSArray *)getMinuteArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour;
/** 获取 secondArr 数组 */
- (NSArray *)getSecondArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
/** 获取 monthWeekArr 数组 */
- (NSArray *)getMonthWeekArr:(NSInteger)year month:(NSInteger)month;
/** 获取 yearWeekArr 数组 */
- (NSArray *)getYearWeekArr:(NSInteger)year;
/** 获取 quarterArr 数组 */
- (NSArray *)getQuarterArr:(NSInteger)year;
/** 添加 pickerView */
- (void)setupPickerView:(UIView *)pickerView toView:(UIView *)view;
/** 设置日期单位 */
- (NSArray *)setupPickerUnitLabel:(UIPickerView *)pickerView unitArr:(NSArray *)unitArr;
- (NSString *)getYearNumber:(NSInteger)year;
- (NSString *)getMDHMSNumber:(NSInteger)number;
- (NSString *)getYearText:(NSArray *)yearArr row:(NSInteger)row;
- (NSString *)getMonthText:(NSArray *)monthArr row:(NSInteger)row;
- (NSString *)getDayText:(NSArray *)dayArr row:(NSInteger)row mSelectDate:(NSDate *)mSelectDate;
- (NSString *)getHourText:(NSArray *)hourArr row:(NSInteger)row;
- (NSString *)getMinuteText:(NSArray *)minuteArr row:(NSInteger)row;
- (NSString *)getSecondText:(NSArray *)secondArr row:(NSInteger)row;
- (NSString *)getWeekText:(NSArray *)weekArr row:(NSInteger)row;
- (NSString *)getQuarterText:(NSArray *)quarterArr row:(NSInteger)row;
- (NSString *)getAMText;
- (NSString *)getPMText;
- (NSString *)getYearUnit;
- (NSString *)getMonthUnit;
- (NSString *)getDayUnit;
- (NSString *)getHourUnit;
- (NSString *)getMinuteUnit;
- (NSString *)getSecondUnit;
- (NSString *)getWeekUnit;
- (NSString *)getQuarterUnit;
- (NSInteger)getIndexWithArray:(NSArray *)array object:(NSString *)obj;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,804 @@
//
// BRDatePickerView+BR.m
// BRPickerViewDemo
//
// Created by renbo on 2020/6/16.
// Copyright © 2020 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "BRDatePickerView+BR.h"
#import "NSBundle+BRPickerView.h"
BRSYNTH_DUMMY_CLASS(BRDatePickerView_BR)
//////////////////////////////////////////
/// BRDatePickerView
//////////////////////////////////////////
@implementation BRDatePickerView (BR)
#pragma mark -
- (NSDate *)handlerMinDate:(NSDate *)minDate {
if (!minDate) {
if (self.pickerMode == BRDatePickerModeMDHM) {
minDate = [NSDate br_setMonth:1 day:1 hour:0 minute:0];
} else if (self.pickerMode == BRDatePickerModeMD) {
minDate = [NSDate br_setMonth:1 day:1];
} else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
minDate = [NSDate br_setHour:0 minute:0];
} else if (self.pickerMode == BRDatePickerModeHMS) {
minDate = [NSDate br_setHour:0 minute:0 second:0];
} else if (self.pickerMode == BRDatePickerModeMS) {
minDate = [NSDate br_setMinute:0 second:0];
} else {
minDate = [NSDate distantPast]; //
}
}
// 12hour1
if (self.isTwelveHourMode) {
minDate = [minDate br_setTwelveHour:1];
}
return minDate;
}
#pragma mark -
- (NSDate *)handlerMaxDate:(NSDate *)maxDate {
if (!maxDate) {
if (self.pickerMode == BRDatePickerModeMDHM) {
maxDate = [NSDate br_setMonth:12 day:31 hour:23 minute:59];
} else if (self.pickerMode == BRDatePickerModeMD) {
maxDate = [NSDate br_setMonth:12 day:31];
} else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
maxDate = [NSDate br_setHour:23 minute:59];
} else if (self.pickerMode == BRDatePickerModeHMS) {
maxDate = [NSDate br_setHour:23 minute:59 second:59];
} else if (self.pickerMode == BRDatePickerModeMS) {
maxDate = [NSDate br_setMinute:59 second:59];
} else {
maxDate = [NSDate distantFuture]; //
}
}
// 12hour12
if (self.isTwelveHourMode) {
maxDate = [maxDate br_setTwelveHour:12];
}
return maxDate;
}
#pragma mark -
- (NSDate *)handlerSelectDate:(NSDate *)selectDate dateFormat:(NSString *)dateFormat {
// selectDate selectValue使 selectDate
if (!selectDate) {
if (self.selectValue && self.selectValue.length > 0) {
if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
NSString *firstString = [[self.selectValue componentsSeparatedByString:@" "] firstObject];
NSString *lastString = [[self.selectValue componentsSeparatedByString:@" "] lastObject];
if ([lastString isEqualToString:[self getAMText]]) {
self.selectValue = [NSString stringWithFormat:@"%@ 00", firstString];
}
if ([lastString isEqualToString:[self getPMText]]) {
self.selectValue = [NSString stringWithFormat:@"%@ 12", firstString];
}
}
NSDate *date = nil;
if ([self.selectValue isEqualToString:self.lastRowContent]) {
date = self.addToNow ? [NSDate date] : nil;
} else if ([self.selectValue isEqualToString:self.firstRowContent]) {
date = nil;
} else {
date = [self br_dateFromString:self.selectValue dateFormat:dateFormat];
if (!date) {
BRErrorLog(@"参数异常!字符串 selectValue 的正确格式是:%@", dateFormat);
date = [NSDate date]; // / Release
}
if (self.pickerMode == BRDatePickerModeMDHM) {
selectDate = [NSDate br_setMonth:date.br_month day:date.br_day hour:date.br_hour minute:date.br_minute];
} else if (self.pickerMode == BRDatePickerModeMD) {
selectDate = [NSDate br_setMonth:date.br_month day:date.br_day];
} else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
selectDate = [NSDate br_setHour:date.br_hour minute:date.br_minute];
} else if (self.pickerMode == BRDatePickerModeHMS) {
selectDate = [NSDate br_setHour:date.br_hour minute:date.br_minute second:date.br_second];
} else if (self.pickerMode == BRDatePickerModeMS) {
selectDate = [NSDate br_setMinute:date.br_minute second:date.br_second];
} else {
selectDate = date;
}
}
} else {
//
if (self.pickerMode == BRDatePickerModeTime ||
self.pickerMode == BRDatePickerModeCountDownTimer ||
self.pickerMode == BRDatePickerModeHM ||
self.pickerMode == BRDatePickerModeHMS ||
self.pickerMode == BRDatePickerModeMS) {
//
selectDate = self.minDate;
} else {
if (self.minuteInterval > 1 || self.secondInterval > 1) {
NSDate *date = [NSDate date];
NSInteger minute = self.minDate.br_minute % self.minuteInterval == 0 ? self.minDate.br_minute : 0;
NSInteger second = self.minDate.br_second % self.secondInterval == 0 ? self.minDate.br_second : 0;
selectDate = [NSDate br_setYear:date.br_year month:date.br_month day:date.br_day hour:date.br_hour minute:minute second:second];
} else {
//
selectDate = [NSDate date];
}
}
}
}
if (!selectDate) {
return nil;
}
//
BOOL selectLessThanMin = [self br_compareDate:selectDate targetDate:self.minDate dateFormat:dateFormat] == NSOrderedAscending;
BOOL selectMoreThanMax = [self br_compareDate:selectDate targetDate:self.maxDate dateFormat:dateFormat] == NSOrderedDescending;
if (selectLessThanMin) {
BRErrorLog(@"默认选择的日期不能小于最小日期!");
selectDate = self.minDate;
}
if (selectMoreThanMax) {
BRErrorLog(@"默认选择的日期不能大于最大日期!");
selectDate = self.maxDate;
}
return selectDate;
}
#pragma mark - NSDate NSString
- (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat {
return [NSDate br_stringFromDate:date dateFormat:dateFormat language:self.pickerStyle.language];
}
#pragma mark - NSString NSDate
- (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat {
return [NSDate br_dateFromString:dateString dateFormat:dateFormat language:self.pickerStyle.language];
}
#pragma mark -
- (NSComparisonResult)br_compareDate:(NSDate *)date targetDate:(NSDate *)targetDate dateFormat:(NSString *)dateFormat {
NSString *dateString1 = [self br_stringFromDate:date dateFormat:dateFormat];
NSString *dateString2 = [self br_stringFromDate:targetDate dateFormat:dateFormat];
NSDate *date1 = [self br_dateFromString:dateString1 dateFormat:dateFormat];
NSDate *date2 = [self br_dateFromString:dateString2 dateFormat:dateFormat];
if ([date1 compare:date2] == NSOrderedDescending) {
return 1; //
} else if ([date1 compare:date2] == NSOrderedAscending) {
return -1; //
} else {
return 0; //
}
}
#pragma mark - yearArr
- (NSArray *)getYearArr {
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = self.minDate.br_year; i <= self.maxDate.br_year; i++) {
[tempArr addObject:[self getYearNumber:i]];
}
if (self.isDescending) {
NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
tempArr = [reversedArr mutableCopy];
}
//
if (self.lastRowContent || self.firstRowContent) {
switch (self.pickerMode) {
case BRDatePickerModeYMDHMS:
case BRDatePickerModeYMDHM:
case BRDatePickerModeYMDH:
case BRDatePickerModeYMD:
case BRDatePickerModeYM:
case BRDatePickerModeY:
case BRDatePickerModeYMW:
case BRDatePickerModeYW:
case BRDatePickerModeYQ:
{
if (self.lastRowContent) {
[tempArr addObject:self.lastRowContent];
}
if (self.firstRowContent) {
[tempArr insertObject:self.firstRowContent atIndex:0];
}
}
break;
default:
break;
}
}
return [tempArr copy];
}
#pragma mark - monthArr
- (NSArray *)getMonthArr:(NSInteger)year {
NSInteger startMonth = 1;
NSInteger endMonth = 12;
if (year == self.minDate.br_year) {
startMonth = self.minDate.br_month;
}
if (year == self.maxDate.br_year) {
endMonth = self.maxDate.br_month;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startMonth; i <= endMonth; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
tempArr = [reversedArr mutableCopy];
}
//
if (self.lastRowContent || self.firstRowContent) {
switch (self.pickerMode) {
case BRDatePickerModeMDHM:
case BRDatePickerModeMD:
{
if (self.lastRowContent) {
[tempArr addObject:self.lastRowContent];
}
if (self.firstRowContent) {
[tempArr insertObject:self.firstRowContent atIndex:0];
}
}
break;
default:
break;
}
}
return [tempArr copy];
}
#pragma mark - dayArr
- (NSArray *)getDayArr:(NSInteger)year month:(NSInteger)month {
NSInteger startDay = 1;
NSInteger endDay = [NSDate br_getDaysInYear:year month:month];
if (year == self.minDate.br_year && month == self.minDate.br_month) {
startDay = self.minDate.br_day;
}
if (year == self.maxDate.br_year && month == self.maxDate.br_month) {
endDay = self.maxDate.br_day;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startDay; i <= endDay; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
return [[tempArr reverseObjectEnumerator] allObjects];
}
return [tempArr copy];
}
#pragma mark - hourArr
- (NSArray *)getHourArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
return @[[self getAMText], [self getPMText]];
}
NSInteger startHour = self.isTwelveHourMode ? 1 : 0;
NSInteger endHour = self.isTwelveHourMode ? 12 : 23;
if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day) {
startHour = self.minDate.br_hour;
if (self.isTwelveHourMode) {
if (startHour < 1 || startHour > 12) {
startHour = 1;
}
}
}
if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day) {
endHour = self.maxDate.br_hour;
if (self.isTwelveHourMode) {
if (endHour < 1 || endHour > 12) {
endHour = 12;
}
}
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startHour; i <= endHour; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
tempArr = [reversedArr mutableCopy];
}
//
if (self.lastRowContent || self.firstRowContent) {
switch (self.pickerMode) {
case BRDatePickerModeHMS:
case BRDatePickerModeHM:
{
if (self.lastRowContent) {
[tempArr addObject:self.lastRowContent];
}
if (self.firstRowContent) {
[tempArr insertObject:self.firstRowContent atIndex:0];
}
}
break;
default:
break;
}
}
return [tempArr copy];
}
#pragma mark - minuteArr
- (NSArray *)getMinuteArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour {
NSInteger startMinute = 0;
NSInteger endMinute = 59;
if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day && hour == self.minDate.br_hour) {
startMinute = self.minDate.br_minute;
}
if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day && hour == self.maxDate.br_hour) {
endMinute = self.maxDate.br_minute;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startMinute; i <= endMinute; i += self.minuteInterval) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
tempArr = [reversedArr mutableCopy];
}
//
if (self.lastRowContent || self.firstRowContent) {
switch (self.pickerMode) {
case BRDatePickerModeMS:
{
if (self.lastRowContent) {
[tempArr addObject:self.lastRowContent];
}
if (self.firstRowContent) {
[tempArr insertObject:self.firstRowContent atIndex:0];
}
}
break;
default:
break;
}
}
return [tempArr copy];
}
#pragma mark - secondArr
- (NSArray *)getSecondArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
NSInteger startSecond = 0;
NSInteger endSecond = 59;
if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day && hour == self.minDate.br_hour && minute == self.minDate.br_minute) {
startSecond = self.minDate.br_second;
}
if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day && hour == self.maxDate.br_hour && minute == self.maxDate.br_minute) {
endSecond = self.maxDate.br_second;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startSecond; i <= endSecond; i += self.secondInterval) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
return [[tempArr reverseObjectEnumerator] allObjects];
}
return [tempArr copy];
}
#pragma mark - monthWeekArr
- (NSArray *)getMonthWeekArr:(NSInteger)year month:(NSInteger)month {
NSInteger startWeek = 1;
NSInteger endWeek = [NSDate br_getWeeksOfMonthInYear:year month:month];
if (year == self.minDate.br_year && month == self.minDate.br_month) {
startWeek = self.minDate.br_monthWeek;
}
if (year == self.maxDate.br_year && month == self.maxDate.br_month) {
endWeek = self.maxDate.br_monthWeek;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startWeek; i <= endWeek; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
return [[tempArr reverseObjectEnumerator] allObjects];
}
return [tempArr copy];
}
#pragma mark - yearWeekArr
- (NSArray *)getYearWeekArr:(NSInteger)year {
NSInteger startWeek = 1;
NSInteger endWeek = [NSDate br_getWeeksOfYearInYear:year];
if (year == self.minDate.br_year) {
startWeek = self.minDate.br_yearWeek;
}
if (year == self.maxDate.br_year) {
endWeek = self.maxDate.br_yearWeek;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startWeek; i <= endWeek; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
return [[tempArr reverseObjectEnumerator] allObjects];
}
return [tempArr copy];
}
#pragma mark - quarterArr
- (NSArray *)getQuarterArr:(NSInteger)year {
NSInteger startQuarter = 1;
NSInteger endQuarter = [NSDate br_getQuartersInYear:year];
if (year == self.minDate.br_year) {
startQuarter = self.minDate.br_quarter;
}
if (year == self.maxDate.br_year) {
endQuarter = self.maxDate.br_quarter;
}
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = startQuarter; i <= endQuarter; i++) {
[tempArr addObject:[self getMDHMSNumber:i]];
}
if (self.isDescending) {
return [[tempArr reverseObjectEnumerator] allObjects];
}
return [tempArr copy];
}
#pragma mark - pickerView
- (void)setupPickerView:(UIView *)pickerView toView:(UIView *)view {
if (view) {
// view view 使
[view setNeedsLayout];
[view layoutIfNeeded];
self.frame = view.bounds;
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
[self addSubview:pickerView];
} else {
// iOS16 pickerView frame
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
[self.alertView addSubview:pickerView];
}
}
#pragma mark -
- (NSArray *)setupPickerUnitLabel:(UIPickerView *)pickerView unitArr:(NSArray *)unitArr {
NSMutableArray *tempArr = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < pickerView.numberOfComponents; i++) {
CGFloat componentWidth = [self pickerView:pickerView widthForComponent:i];
// label
UILabel *unitLabel = [[UILabel alloc]init];
unitLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
unitLabel.backgroundColor = [UIColor clearColor];
unitLabel.font = self.pickerStyle.dateUnitTextFont;
unitLabel.textColor = self.pickerStyle.dateUnitTextColor;
//
unitLabel.adjustsFontSizeToFitWidth = YES;
//
unitLabel.minimumScaleFactor = 0.5f;
unitLabel.text = (unitArr.count > 0 && i < unitArr.count) ? unitArr[i] : nil;
[unitLabel sizeToFit];
// lable
NSString *tempText = @"00";
if (i == 0) {
switch (self.pickerMode) {
case BRDatePickerModeYMDHMS:
case BRDatePickerModeYMDHM:
case BRDatePickerModeYMDH:
case BRDatePickerModeYMD:
case BRDatePickerModeYM:
case BRDatePickerModeY:
{
tempText = @"0000";
}
break;
default:
break;
}
}
//
CGFloat labelTextWidth = [tempText boundingRectWithSize:CGSizeMake(MAXFLOAT, self.pickerStyle.rowHeight)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName:self.pickerStyle.selectRowTextFont ?: self.pickerStyle.pickerTextFont}
context:nil].size.width;
CGFloat margin = (pickerView.frame.size.width - componentWidth * pickerView.numberOfComponents) / 2;
CGFloat deltaX = (6 - pickerView.numberOfComponents) * 3 + 2;
CGFloat centerX = margin + componentWidth / 2.0f + i * (componentWidth + 5) + labelTextWidth / 2.0f + deltaX + self.pickerStyle.dateUnitOffsetX;
CGFloat centerY = pickerView.frame.origin.y + pickerView.frame.size.height / 2.0f + self.pickerStyle.dateUnitOffsetY;
unitLabel.center = CGPointMake(centerX, centerY);
[tempArr addObject:unitLabel];
[pickerView.superview addSubview:unitLabel];
}
return [tempArr copy];
}
- (NSString *)getYearNumber:(NSInteger)year {
NSString *yearString = [NSString stringWithFormat:@"%@", @(year)];
if (self.isNumberFullName) {
yearString = [NSString stringWithFormat:@"%04d", [yearString intValue]];
}
return yearString;
}
- (NSString *)getMDHMSNumber:(NSInteger)number {
NSString *string = [NSString stringWithFormat:@"%@", @(number)];
if (self.isNumberFullName) {
string = [NSString stringWithFormat:@"%02d", [string intValue]];
}
return string;
}
- (NSString *)getYearText:(NSArray *)yearArr row:(NSInteger)row {
if (yearArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, yearArr.count - 1);
}
NSString *yearString = [yearArr objectAtIndex:index];
if ((self.lastRowContent && [yearString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [yearString isEqualToString:self.firstRowContent])) {
return yearString;
}
NSString *yearUnit = self.showUnitType == BRShowUnitTypeAll ? [self getYearUnit] : @"";
return [NSString stringWithFormat:@"%@%@", yearString, yearUnit];
}
- (NSString *)getMonthText:(NSArray *)monthArr row:(NSInteger)row {
if (monthArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, monthArr.count - 1);
}
NSString *monthString = [monthArr objectAtIndex:index];
// /
if ((self.firstRowContent && [monthString isEqualToString:self.firstRowContent]) || (self.lastRowContent && [monthString isEqualToString:self.lastRowContent])) {
return monthString;
}
//
if (self.monthNames && self.monthNames.count > 0) {
NSInteger index = [monthString integerValue] - 1;
monthString = (index >= 0 && index < self.monthNames.count) ? self.monthNames[index] : @"";
} else {
if (![self.pickerStyle.language hasPrefix:@"zh"] && (self.pickerMode == BRDatePickerModeYMD || self.pickerMode == BRDatePickerModeYM || self.pickerMode == BRDatePickerModeYMW)) {
// 使
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:self.pickerStyle.language];
// monthSymbols: @[@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"];
// shortMonthSymbols: @[@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"];
NSArray *monthNames = self.isShortMonthName ? dateFormatter.shortMonthSymbols : dateFormatter.monthSymbols;
NSInteger index = [monthString integerValue] - 1;
monthString = (index >= 0 && index < monthNames.count) ? monthNames[index] : @"";
} else {
//
NSString *monthUnit = self.showUnitType == BRShowUnitTypeAll ? [self getMonthUnit] : @"";
monthString = [NSString stringWithFormat:@"%@%@", monthString, monthUnit];
}
}
return monthString;
}
- (NSString *)getDayText:(NSArray *)dayArr row:(NSInteger)row mSelectDate:(NSDate *)mSelectDate {
if (dayArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, dayArr.count - 1);
}
NSString *dayString = [dayArr objectAtIndex:index];
if (self.isShowToday && mSelectDate.br_year == [NSDate date].br_year && mSelectDate.br_month == [NSDate date].br_month && [dayString integerValue] == [NSDate date].br_day) {
return [NSBundle br_localizedStringForKey:@"今天" language:self.pickerStyle.language];
}
NSString *dayUnit = self.showUnitType == BRShowUnitTypeAll ? [self getDayUnit] : @"";
dayString = [NSString stringWithFormat:@"%@%@", dayString, dayUnit];
if (self.isShowWeek) {
NSDate *date = [NSDate br_setYear:mSelectDate.br_year month:mSelectDate.br_month day:[dayString integerValue]];
NSString *weekdayString = [NSBundle br_localizedStringForKey:[date br_weekdayString] language:self.pickerStyle.language];
dayString = [NSString stringWithFormat:@"%@%@", dayString, weekdayString];
}
return dayString;
}
- (NSString *)getHourText:(NSArray *)hourArr row:(NSInteger)row {
if (hourArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, hourArr.count - 1);
}
NSString *hourString = [hourArr objectAtIndex:index];
if ((self.lastRowContent && [hourString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [hourString isEqualToString:self.firstRowContent])) {
return hourString;
}
NSString *hourUnit = self.showUnitType == BRShowUnitTypeAll ? [self getHourUnit] : @"";
return [NSString stringWithFormat:@"%@%@", hourString, hourUnit];
}
- (NSString *)getMinuteText:(NSArray *)minuteArr row:(NSInteger)row {
if (minuteArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, minuteArr.count - 1);
}
NSString *minuteString = [minuteArr objectAtIndex:index];
NSString *minuteUnit = self.showUnitType == BRShowUnitTypeAll ? [self getMinuteUnit] : @"";
return [NSString stringWithFormat:@"%@%@", minuteString, minuteUnit];
}
- (NSString *)getSecondText:(NSArray *)secondArr row:(NSInteger)row {
if (secondArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, secondArr.count - 1);
}
NSString *secondString = [secondArr objectAtIndex:index];
NSString *secondUnit = self.showUnitType == BRShowUnitTypeAll ? [self getSecondUnit] : @"";
return [NSString stringWithFormat:@"%@%@", secondString, secondUnit];
}
- (NSString *)getWeekText:(NSArray *)weekArr row:(NSInteger)row {
if (weekArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, weekArr.count - 1);
}
NSString *weekString = [weekArr objectAtIndex:index];
if ((self.lastRowContent && [weekString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [weekString isEqualToString:self.firstRowContent])) {
return weekString;
}
NSString *weekUnit = self.showUnitType == BRShowUnitTypeAll ? [self getWeekUnit] : @"";
return [NSString stringWithFormat:@"%@%@", weekString, weekUnit];
}
- (NSString *)getQuarterText:(NSArray *)quarterArr row:(NSInteger)row {
if (quarterArr.count == 0) {
return @"";
}
NSInteger index = 0;
if (row >= 0) {
index = MIN(row, quarterArr.count - 1);
}
NSString *quarterString = [quarterArr objectAtIndex:index];
if ((self.lastRowContent && [quarterString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [quarterString isEqualToString:self.firstRowContent])) {
return quarterString;
}
NSString *quarterUnit = self.showUnitType == BRShowUnitTypeAll ? [self getQuarterUnit] : @"";
return [NSString stringWithFormat:@"%@%@", quarterString, quarterUnit];
}
- (NSString *)getAMText {
return [NSBundle br_localizedStringForKey:@"上午" language:self.pickerStyle.language];
}
- (NSString *)getPMText {
return [NSBundle br_localizedStringForKey:@"下午" language:self.pickerStyle.language];
}
- (NSString *)getYearUnit {
if (self.customUnit) {
return self.customUnit[@"year"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"年" language:self.pickerStyle.language];
}
- (NSString *)getMonthUnit {
if (self.customUnit) {
return self.customUnit[@"month"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"月" language:self.pickerStyle.language];
}
- (NSString *)getDayUnit {
if (self.customUnit) {
return self.customUnit[@"day"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"日" language:self.pickerStyle.language];
}
- (NSString *)getHourUnit {
if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
return @"";
}
if (self.customUnit) {
return self.customUnit[@"hour"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"时" language:self.pickerStyle.language];
}
- (NSString *)getMinuteUnit {
if (self.customUnit) {
return self.customUnit[@"minute"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"分" language:self.pickerStyle.language];
}
- (NSString *)getSecondUnit {
if (self.customUnit) {
return self.customUnit[@"second"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"秒" language:self.pickerStyle.language];
}
- (NSString *)getWeekUnit {
if (self.customUnit) {
return self.customUnit[@"week"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"周" language:self.pickerStyle.language];
}
- (NSString *)getQuarterUnit {
if (self.customUnit) {
return self.customUnit[@"quarter"] ? : @"";
}
if (![self.pickerStyle.language hasPrefix:@"zh"]) {
return @"";
}
return [NSBundle br_localizedStringForKey:@"季度" language:self.pickerStyle.language];
}
- (NSInteger)getIndexWithArray:(NSArray *)array object:(NSString *)obj {
if (!array || array.count == 0 || !obj) {
return 0;
}
if ([array containsObject:obj]) {
return [array indexOfObject:obj];
}
return 0;
}
@end

View File

@ -0,0 +1,291 @@
//
// BRDatePickerView.h
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import "BRPickerAlertView.h"
#import "NSDate+BRPickerView.h"
NS_ASSUME_NONNULL_BEGIN
/// 日期选择器格式
typedef NS_ENUM(NSInteger, BRDatePickerMode) {
// ----- 以下4种是系统样式兼容国际化日期格式 -----
/** 【yyyy-MM-dd】UIDatePickerModeDate美式日期MM-dd-yyyy英式日期dd-MM-yyyy*/
BRDatePickerModeDate,
/** 【yyyy-MM-dd HH:mm】 UIDatePickerModeDateAndTime */
BRDatePickerModeDateAndTime,
/** 【HH:mm】UIDatePickerModeTime */
BRDatePickerModeTime,
/** 【HH:mm】UIDatePickerModeCountDownTimer */
BRDatePickerModeCountDownTimer,
// ----- 以下14种是自定义样式 -----
/** 【yyyy-MM-dd HH:mm:ss】年月日时分秒 */
BRDatePickerModeYMDHMS,
/** 【yyyy-MM-dd HH:mm】年月日时分 */
BRDatePickerModeYMDHM,
/** 【yyyy-MM-dd HH】年月日时 */
BRDatePickerModeYMDH,
/** 【MM-dd HH:mm】月日时分 */
BRDatePickerModeMDHM,
/** 【yyyy-MM-dd】年月日兼容国际化日期dd-MM-yyyy*/
BRDatePickerModeYMD,
/** 【yyyy-MM】年月兼容国际化日期MM-yyyy*/
BRDatePickerModeYM,
/** 【yyyy】年 */
BRDatePickerModeY,
/** 【MM-dd】月日 */
BRDatePickerModeMD,
/** 【HH:mm:ss】时分秒 */
BRDatePickerModeHMS,
/** 【HH:mm】时分 */
BRDatePickerModeHM,
/** 【mm:ss】分秒 */
BRDatePickerModeMS,
/** 【yyyy-qq】年季度 */
BRDatePickerModeYQ,
/** 【yyyy-MM-ww】年月周 */
BRDatePickerModeYMW,
/** 【yyyy-ww】年周 */
BRDatePickerModeYW
};
/// 日期单位显示的位置
typedef NS_ENUM(NSInteger, BRShowUnitType) {
/** 日期单位显示全部行(默认)*/
BRShowUnitTypeAll,
/** 日期单位仅显示中间行 */
BRShowUnitTypeOnlyCenter,
/** 日期单位不显示(隐藏日期单位)*/
BRShowUnitTypeNone
};
typedef void (^BRDateResultBlock)(NSDate * _Nullable selectDate, NSString * _Nullable selectValue);
typedef void (^BRDateResultRangeBlock)(NSDate * _Nullable selectStartDate, NSDate * _Nullable selectEndDate, NSString * _Nullable selectValue);
@interface BRDatePickerView : BRPickerAlertView
/**
//////////////////////////////////////////////////////////////////////////
///
/// 【用法一】
/// 特点:灵活,扩展性强(推荐使用!)
///
////////////////////////////////////////////////////////////////////////*/
/** 日期选择器显示类型 */
@property (nonatomic, assign) BRDatePickerMode pickerMode;
/** 设置选中的日期(推荐使用 selectDate */
@property (nullable, nonatomic, strong) NSDate *selectDate;
@property (nullable, nonatomic, copy) NSString *selectValue;
/** 最小日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)*/
@property (nullable, nonatomic, strong) NSDate *minDate;
/** 最大日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)*/
@property (nullable, nonatomic, strong) NSDate *maxDate;
/** 是否自动选择,即滚动选择器后就执行结果回调,默认为 NO */
@property (nonatomic, assign) BOOL isAutoSelect;
/** 选择结果的回调 */
@property (nullable, nonatomic, copy) BRDateResultBlock resultBlock;
/** 选择结果范围的回调for `BRDatePickerModeYQ`、`BRDatePickerModeYMW`、`BRDatePickerModeYW`, ignored otherwise. */
@property (nullable, nonatomic, copy) BRDateResultRangeBlock resultRangeBlock;
/** 滚动选择时触发的回调 */
@property (nullable, nonatomic, copy) BRDateResultBlock changeBlock;
/** 滚动选择范围时触发的回调for `BRDatePickerModeYQ`、`BRDatePickerModeYMW`、`BRDatePickerModeYW`, ignored otherwise. */
@property (nullable, nonatomic, copy) BRDateResultRangeBlock changeRangeBlock;
/** 判断选择器是否处于滚动中。可以用于解决快速滑动选择器,在滚动结束前秒选确定按钮,出现显示不对的问题。*/
@property (nonatomic, readonly, assign, getter=isRolling) BOOL rolling;
/** 日期单位显示类型 */
@property (nonatomic, assign) BRShowUnitType showUnitType;
/** 是否显示【星期】,默认为 NO */
@property (nonatomic, assign, getter=isShowWeek) BOOL showWeek;
/** 是否显示【今天】,默认为 NO */
@property (nonatomic, assign, getter=isShowToday) BOOL showToday;
/** 是否添加【至今】,默认为 NO */
@property (nonatomic, assign, getter=isAddToNow) BOOL addToNow;
/** 首行添加 自定义字符串,配合 selectValue 可设置默认选中 */
@property (nullable, nonatomic, copy) NSString *firstRowContent;
/** 末行添加 自定义字符串(如:至今),配合 selectValue 可设置默认选中 */
@property (nullable, nonatomic, copy) NSString *lastRowContent;
/** 滚轮上日期数据排序是否降序,默认为 NO升序*/
@property (nonatomic, assign, getter=isDescending) BOOL descending;
/** 选择器上数字是否带有前导零,默认为 NO无前导零:2020-1-1有前导零:2020-01-01*/
@property (nonatomic, assign, getter=isNumberFullName) BOOL numberFullName;
/** 是否为12小时制默认为NO */
@property (nonatomic, assign, getter=isTwelveHourMode) BOOL twelveHourMode;
/** 设置分的时间间隔默认为1范围1 ~ 30*/
@property (nonatomic, assign) NSInteger minuteInterval;
/** 设置秒的时间间隔默认为1范围1 ~ 30*/
@property (nonatomic, assign) NSInteger secondInterval;
/** 设置倒计时的时长默认为0范围0 ~ 24*60*60-1单位为秒 for `BRDatePickerModeCountDownTimer`, ignored otherwise. */
@property (nonatomic, assign) NSTimeInterval countDownDuration;
/**
*
* @[@"1月", @"2月",..., @"12月"] @[@"一月", @"二月",..., @"十二月"] @[@"Jan", @"Feb",..., @"Dec"]
*/
@property (nonatomic, copy) NSArray <NSString *> *monthNames;
/**
* () NOfor `BRDatePickerModeYMD` and `BRDatePickerModeYM`, ignored otherwise.
* January Jan
*/
@property (nonatomic, assign, getter=isShortMonthName) BOOL shortMonthName;
/**
*
* @{@"year": @"", @"month": @"", @"day": @"", @"hour": @"", @"minute": @"", @"second": @""}
*/
@property (nonatomic, copy) NSDictionary *customUnit;
/** 显示上午和下午,默认为 NO. for `BRDatePickerModeYMDH`, ignored otherwise. */
@property (nonatomic, assign, getter=isShowAMAndPM) BOOL showAMAndPM;
/**
*
* timeZone = [NSTimeZone timeZoneWithName:@"America/New_York"]; // 如:设置时区为 美国纽约
* 使 NSDate+BRPickerView minDatemaxDate等 NSDate+BRPickerView
*/
@property (nullable, nonatomic, copy) NSTimeZone *timeZone;
/**
* default is [NSCalendar currentCalendar]. setting nil returns to default. for `UIDatePicker`, ignored otherwise.
* calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierChinese]; // 设置中国农历(阴历)日期
*/
@property (nullable, nonatomic, copy) NSCalendar *calendar;
/** 指定不允许选择的日期 */
@property (nullable, nonatomic, copy) NSArray <NSDate *> *nonSelectableDates;
/** 不允许选择日期的回调 */
@property (nullable, nonatomic, copy) BRDateResultBlock nonSelectableBlock;
/// 初始化日期选择器
/// @param pickerMode 日期选择器显示类型
- (instancetype)initWithPickerMode:(BRDatePickerMode)pickerMode;
/// 弹出选择器视图
- (void)show;
/// 关闭选择器视图
- (void)dismiss;
//================================================= 华丽的分割线 =================================================
/**
//////////////////////////////////////////////////////////////////////////
///
/// 【用法二】:快捷使用,直接选择下面其中的一个方法进行使用
/// 特点:快捷,方便
///
////////////////////////////////////////////////////////////////////////*/
/**
* 1.
*
* @param mode
* @param title
* @param selectValue
* @param resultBlock
*
*/
+ (void)showDatePickerWithMode:(BRDatePickerMode)mode
title:(nullable NSString *)title
selectValue:(nullable NSString *)selectValue
resultBlock:(nullable BRDateResultBlock)resultBlock;
/**
* 2.
*
* @param mode
* @param title
* @param selectValue
* @param isAutoSelect NO
* @param resultBlock
*
*/
+ (void)showDatePickerWithMode:(BRDatePickerMode)mode
title:(nullable NSString *)title
selectValue:(nullable NSString *)selectValue
isAutoSelect:(BOOL)isAutoSelect
resultBlock:(nullable BRDateResultBlock)resultBlock;
/**
* 3.
*
* @param mode
* @param title
* @param selectValue
* @param minDate 使 NSDate+BRPickerView
* @param maxDate 使 NSDate+BRPickerView
* @param isAutoSelect NO
* @param resultBlock
*
*/
+ (void)showDatePickerWithMode:(BRDatePickerMode)mode
title:(nullable NSString *)title
selectValue:(nullable NSString *)selectValue
minDate:(nullable NSDate *)minDate
maxDate:(nullable NSDate *)maxDate
isAutoSelect:(BOOL)isAutoSelect
resultBlock:(nullable BRDateResultBlock)resultBlock;
/**
* 4.
*
* @param mode
* @param title
* @param selectValue
* @param minDate 使 NSDate+BRPickerView
* @param maxDate 使 NSDate+BRPickerView
* @param isAutoSelect NO
* @param resultBlock
* @param resultRangeBlock
*
*/
+ (void)showDatePickerWithMode:(BRDatePickerMode)mode
title:(nullable NSString *)title
selectValue:(nullable NSString *)selectValue
minDate:(nullable NSDate *)minDate
maxDate:(nullable NSDate *)maxDate
isAutoSelect:(BOOL)isAutoSelect
resultBlock:(nullable BRDateResultBlock)resultBlock
resultRangeBlock:(nullable BRDateResultRangeBlock)resultRangeBlock;
// 获取列宽(组件内部方法)
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
//
// NSDate+BRPickerView.h
// BRPickerViewDemo
//
// Created by renbo on 2018/3/15.
// Copyright © 2018 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSDate (BRPickerView)
/// ---------------- 设置【日历对象】和【时区】 ----------------
/** 设置日历对象 */
+ (void)br_setCalendar:(NSCalendar *)calendar;
/** 获取日历对象 */
+ (NSCalendar *)br_getCalendar;
/** 设置时区 */
+ (void)br_setTimeZone:(NSTimeZone *)timeZone;
/** 获取当前时区 */
+ (NSTimeZone *)br_getTimeZone;
/// 获取指定date的详细信息
@property (readonly) NSInteger br_year; // 年
@property (readonly) NSInteger br_month; // 月
@property (readonly) NSInteger br_day; // 日
@property (readonly) NSInteger br_hour; // 时
@property (readonly) NSInteger br_minute; // 分
@property (readonly) NSInteger br_second; // 秒
@property (readonly) NSInteger br_weekday; // 星期
@property (readonly) NSInteger br_monthWeek; // 月周
@property (readonly) NSInteger br_yearWeek; // 年周
@property (readonly) NSInteger br_quarter; // 季度
/** 获取中文星期字符串 */
@property (nullable, nonatomic, readonly, copy) NSString *br_weekdayString;
/// ---------------- 创建 date ----------------
/** yyyy */
+ (nullable NSDate *)br_setYear:(NSInteger)year;
/** yyyy-MM */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month;
/** yyyy-MM-dd */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
/** yyyy-MM-dd HH */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour;
/** yyyy-MM-dd HH:mm */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
/** yyyy-MM-dd HH:mm:ss */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
/** MM-dd HH:mm */
+ (nullable NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
/** MM-dd */
+ (nullable NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day;
/** HH:mm:ss */
+ (nullable NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
/** HH:mm */
+ (nullable NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute;
/** mm:ss */
+ (nullable NSDate *)br_setMinute:(NSInteger)minute second:(NSInteger)second;
/** yyyy-MM-ww */
+ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month weekOfMonth:(NSInteger)weekOfMont;
/** yyyy-ww */
+ (nullable NSDate *)br_setYear:(NSInteger)year weekOfYear:(NSInteger)weekOfYear;
/** yyyy-qq */
+ (nullable NSDate *)br_setYear:(NSInteger)year quarter:(NSInteger)quarter;
/** 设置12小时制时的hour值 */
- (NSDate *)br_setTwelveHour:(NSInteger)hour;
/** 获取某个月的天数(通过年月求每月天数)*/
+ (NSUInteger)br_getDaysInYear:(NSInteger)year month:(NSInteger)month;
/** 获取某个月的周数(通过年月求该月周数)*/
+ (NSUInteger)br_getWeeksOfMonthInYear:(NSInteger)year month:(NSInteger)month;
/** 获取某一年的周数(通过年求该年周数)*/
+ (NSUInteger)br_getWeeksOfYearInYear:(NSInteger)year;
/** 获取某一年的季度数(通过年求该年季度数)*/
+ (NSUInteger)br_getQuartersInYear:(NSInteger)year;
/** 获取 日期加上/减去某天数后的新日期 */
- (nullable NSDate *)br_getNewDateToDays:(NSTimeInterval)days;
/** 获取 日期加上/减去某个月数后的新日期 */
- (nullable NSDate *)br_getNewDateToMonths:(NSTimeInterval)months;
/** NSDate 转 NSString使用系统默认 时区 和 语言)*/
+ (nullable NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat;
/** NSDate 转 NSString */
+ (nullable NSString *)br_stringFromDate:(NSDate *)date
dateFormat:(NSString *)dateFormat
language:(nullable NSString *)language;
/** NSString 转 NSDate使用系统默认 时区 和 语言)*/
+ (nullable NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat;
/** NSString 转 NSDate */
+ (nullable NSDate *)br_dateFromString:(NSString *)dateString
dateFormat:(NSString *)dateFormat
language:(nullable NSString *)language;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,398 @@
//
// NSDate+BRPickerView.m
// BRPickerViewDemo
//
// Created by renbo on 2018/3/15.
// Copyright © 2018 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "NSDate+BRPickerView.h"
#import "BRPickerViewMacro.h"
BRSYNTH_DUMMY_CLASS(NSDate_BRPickerView)
static const NSCalendarUnit unitFlags = (NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekOfMonth | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal | NSCalendarUnitQuarter);
@implementation NSDate (BRPickerView)
///
static NSCalendar *_sharedCalendar = nil;
///
static NSTimeZone *_timeZone = nil;
/// ---------------- ----------------
#pragma mark -
+ (void)br_setCalendar:(NSCalendar *)calendar {
_sharedCalendar = calendar;
}
#pragma mark -
+ (NSCalendar *)br_getCalendar {
if (!_sharedCalendar) {
// /
_sharedCalendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
}
return _sharedCalendar;
}
#pragma mark -
+ (void)br_setTimeZone:(NSTimeZone *)timeZone {
_timeZone = timeZone;
//
[self br_getCalendar].timeZone = timeZone;
}
#pragma mark -
+ (NSTimeZone *)br_getTimeZone {
if (!_timeZone) {
//
NSTimeZone *localTimeZone = [NSTimeZone localTimeZone];
// GMT()
NSInteger interval = [localTimeZone secondsFromGMTForDate:[NSDate date]];
// (使)NSTimeZone
// NSString NSDate NSDateFormatter null
_timeZone = [NSTimeZone timeZoneForSecondsFromGMT:interval];
}
return _timeZone;
}
#pragma mark - NSDate NSDateComponents
+ (NSDateComponents *)br_componentsFromDate:(NSDate *)date {
// NSCalendar
NSCalendar *calendar = [self br_getCalendar];
// NSDateComponents
return [calendar components:unitFlags fromDate:date];
}
#pragma mark - NSDateComponents NSDate
+ (NSDate *)br_dateFromComponents:(NSDateComponents *)components {
// NSCalendar
NSCalendar *calendar = [self br_getCalendar];
return [calendar dateFromComponents:components];
}
#pragma mark -
- (NSInteger)br_year {
return [NSDate br_componentsFromDate:self].year;
}
#pragma mark -
- (NSInteger)br_month {
return [NSDate br_componentsFromDate:self].month;
}
#pragma mark -
- (NSInteger)br_day {
return [NSDate br_componentsFromDate:self].day;
}
#pragma mark -
- (NSInteger)br_hour {
return [NSDate br_componentsFromDate:self].hour;
}
#pragma mark -
- (NSInteger)br_minute {
return [NSDate br_componentsFromDate:self].minute;
}
#pragma mark -
- (NSInteger)br_second {
return [NSDate br_componentsFromDate:self].second;
}
#pragma mark -
- (NSInteger)br_weekday {
return [NSDate br_componentsFromDate:self].weekday;
}
#pragma mark -
- (NSInteger)br_monthWeek {
return [NSDate br_componentsFromDate:self].weekOfMonth;
}
#pragma mark -
- (NSInteger)br_yearWeek {
return [NSDate br_componentsFromDate:self].weekOfYear;
}
#pragma mark -
- (NSInteger)br_quarter {
// [NSDate br_componentsFromDate:self].quarter; // 0?
NSInteger quarter = 1;
NSInteger month = self.br_month;
if (month > 3) quarter = 2;
if (month > 6) quarter = 3;
if (month > 9) quarter = 4;
return quarter;
}
#pragma mark -
- (NSString *)br_weekdayString {
switch (self.br_weekday - 1) {
case 0:
{
return @"周日";
}
break;
case 1:
{
return @"周一";
}
break;
case 2:
{
return @"周二";
}
break;
case 3:
{
return @"周三";
}
break;
case 4:
{
return @"周四";
}
break;
case 5:
{
return @"周五";
}
break;
case 6:
{
return @"周六";
}
break;
default:
break;
}
return @"";
}
/// ---------------- date ----------------
#pragma mark - date NSCalendar
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
return [self br_setYear:year month:month day:day hour:hour minute:minute second:second weekOfMonth:0 weekOfYear:0 quarter:0];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day
hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second
weekOfMonth:(NSInteger)weekOfMonth weekOfYear:(NSInteger)weekOfYear quarter:(NSInteger)quarter {
NSDateComponents *components = [self br_componentsFromDate:[NSDate date]];
if (year > 0) {
//
components = [[NSDateComponents alloc]init];
components.year = year;
}
if (month > 0) {
components.month = month;
}
if (day > 0) {
components.day = day;
}
if (hour >= 0) {
components.hour = hour;
}
if (minute >= 0) {
components.minute = minute;
}
if (second >= 0) {
components.second = second;
}
if (weekOfMonth > 0) {
components.weekOfMonth = weekOfMonth;
}
if (weekOfYear > 0) {
components.weekOfYear = weekOfYear;
}
if (quarter > 0) {
components.quarter = quarter;
}
return [self br_dateFromComponents:components];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
return [self br_setYear:year month:month day:day hour:hour minute:minute second:0];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour {
return [self br_setYear:year month:month day:day hour:hour minute:0 second:0];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
return [self br_setYear:year month:month day:day hour:0 minute:0 second:0];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month {
return [self br_setYear:year month:month day:0 hour:0 minute:0 second:0];
}
+ (NSDate *)br_setYear:(NSInteger)year {
return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0];
}
+ (NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
return [self br_setYear:0 month:month day:day hour:hour minute:minute second:0];
}
+ (NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day {
return [self br_setYear:0 month:month day:day hour:0 minute:0 second:0];
}
+ (NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
return [self br_setYear:0 month:0 day:0 hour:hour minute:minute second:second];
}
+ (NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute {
return [self br_setYear:0 month:0 day:0 hour:hour minute:minute second:0];
}
+ (NSDate *)br_setMinute:(NSInteger)minute second:(NSInteger)second {
return [self br_setYear:0 month:0 day:0 hour:0 minute:minute second:second];
}
+ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month weekOfMonth:(NSInteger)weekOfMonth {
return [self br_setYear:year month:month day:0 hour:0 minute:0 second:0 weekOfMonth:weekOfMonth weekOfYear:0 quarter:0];
}
+ (NSDate *)br_setYear:(NSInteger)year weekOfYear:(NSInteger)weekOfYear {
return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0 weekOfMonth:0 weekOfYear:weekOfYear quarter:0];
}
+ (NSDate *)br_setYear:(NSInteger)year quarter:(NSInteger)quarter {
return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0 weekOfMonth:0 weekOfYear:0 quarter:quarter];
}
- (NSDate *)br_setTwelveHour:(NSInteger)hour {
NSDateComponents *components = [NSDate br_componentsFromDate:self];
if (hour >= 0) {
components.hour = hour;
}
return [NSDate br_dateFromComponents:components];
}
#pragma mark -
+ (NSUInteger)br_getDaysInYear:(NSInteger)year month:(NSInteger)month {
BOOL isLeapYear = year % 4 == 0 ? (year % 100 == 0 ? (year % 400 == 0 ? YES : NO) : YES) : NO;
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
{
return 31;
}
case 4:
case 6:
case 9:
case 11:
{
return 30;
}
case 2:
{
if (isLeapYear) {
return 29;
} else {
return 28;
}
}
default:
break;
}
return 0;
}
#pragma mark -
+ (NSUInteger)br_getWeeksOfMonthInYear:(NSInteger)year month:(NSInteger)month {
NSUInteger lastDayOfMonth = [self br_getDaysInYear:year month:month];
NSDate *endDate = [self br_setYear:year month:month day:lastDayOfMonth];
return endDate.br_monthWeek;
}
#pragma mark -
+ (NSUInteger)br_getWeeksOfYearInYear:(NSInteger)year {
NSDate *endDate = [self br_setYear:year month:12 day:31];
NSInteger weeks = endDate.br_yearWeek;
if (weeks == 1) weeks = 52;
return weeks;
}
#pragma mark -
+ (NSUInteger)br_getQuartersInYear:(NSInteger)year {
NSDate *endDate = [self br_setYear:year month:12 day:31];
return endDate.br_quarter;
}
#pragma mark - /
- (NSDate *)br_getNewDateToDays:(NSTimeInterval)days {
// days
return [self dateByAddingTimeInterval:60 * 60 * 24 * days];
}
#pragma mark - /
- (nullable NSDate *)br_getNewDateToMonths:(NSTimeInterval)months {
// months
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setMonth:months];
NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
return [calender dateByAddingComponents:components toDate:self options:0];
}
#pragma mark - NSDate NSString
+ (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat {
return [self br_stringFromDate:date dateFormat:dateFormat language:nil];
}
#pragma mark - NSDate NSString
+ (NSString *)br_stringFromDate:(NSDate *)date
dateFormat:(NSString *)dateFormat
language:(NSString *)language {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
//
dateFormatter.dateFormat = dateFormat;
// NSDateFormatter
dateFormatter.timeZone = [self br_getTimeZone];
if (!language) {
language = [NSLocale preferredLanguages].firstObject;
}
dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:language];
NSString *dateString = [dateFormatter stringFromDate:date];
return dateString;
}
#pragma mark - NSString NSDate
+ (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat {
return [self br_dateFromString:dateString dateFormat:dateFormat language:nil];
}
#pragma mark - NSString NSDate
+ (NSDate *)br_dateFromString:(NSString *)dateString
dateFormat:(NSString *)dateFormat
language:(NSString *)language {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
//
dateFormatter.dateFormat = dateFormat;
if (!language) {
language = [NSLocale preferredLanguages].firstObject;
}
//
dateFormatter.timeZone = [self br_getTimeZone];
dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:language];
//
dateFormatter.lenient = YES;
return [dateFormatter dateFromString:dateString];
}
@end

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
</dict>
</plist>

View File

@ -0,0 +1,62 @@
//
// BRTextModel.h
// BRPickerViewDemo
//
// Created by renbo on 2019/10/2.
// Copyright © 2019 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BRTextModel : NSObject
/** code */
@property (nullable, nonatomic, copy) NSString *code;
/** text */
@property (nullable, nonatomic, copy) NSString *text;
/** 子级 list */
@property (nullable, nonatomic, copy) NSArray<BRTextModel *> *children;
/** 父级 code */
@property (nonatomic, strong) NSString *parentCode;
/** 其它扩展字段 */
@property (nullable, nonatomic, strong) id extras;
/** 记录选择的索引位置 */
@property (nonatomic, assign) NSInteger index;
/** 字典 转 模型 */
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
@end
/// 工具方法
@interface NSArray (BRPickerView)
/// 数组 转 模型数组
/// - Parameters:
/// - dataArr: 字典数组
/// - mapper: 指定 BRTextModel模型的属性 与 字典key 的映射关系
+ (NSArray *)br_modelArrayWithJson:(NSArray *)dataArr mapper:(nullable NSDictionary *)mapper;
/// 获取模型数组元素,指定属性的值组成新数组
/// - Parameter propertyName: 模型的属性名称
- (NSArray *)br_getValueArr:(NSString *)propertyName;
/// 将模型数组元素,指定属性连接成字符串
/// - Parameters:
/// - propertyName: 模型的属性名称
/// - separator: 分隔符
- (NSString *)br_joinValue:(NSString *)propertyName separator:(NSString *)separator;
/// 将模型数组元素的 text 属性,连接成字符串
/// - Parameter separator: 分隔符
- (NSString *)br_joinText:(NSString *)separator;
/// 将扁平结构模型数组 转换成 树状结构模型数组
- (NSArray<BRTextModel *> *)br_buildTreeArray;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,183 @@
//
// BRTextModel.m
// BRPickerViewDemo
//
// Created by renbo on 2019/10/2.
// Copyright © 2019 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "BRTextModel.h"
@implementation BRTextModel
///
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super init]) {
self.code = dictionary[@"code"];
self.text = dictionary[@"text"];
self.parentCode = dictionary[@"parent_code"];
NSArray *childrenArray = dictionary[@"children"];
if (childrenArray) {
NSMutableArray *tempArr = [NSMutableArray array];
for (NSDictionary *childDict in childrenArray) {
BRTextModel *child = [[BRTextModel alloc] initWithDictionary:childDict];
[tempArr addObject:child];
}
self.children = [tempArr copy];
}
}
return self;
}
///
/// @param object
- (BOOL)isEqual:(id)object {
// 1.
if (self == object) {
return YES;
}
if (![object isKindOfClass:[BRTextModel class]]) {
return NO;
}
BRTextModel *model = (BRTextModel *)object;
if (!model) {
return NO;
}
// 2.
BOOL isSameCode = (!self.code && !model.code) || [self.code isEqualToString:model.code];
BOOL isSameText = (!self.text && !model.text) || [self.text isEqualToString:model.text];
return isSameCode && isSameText;
}
- (NSUInteger)hash {
return [self.code hash] ^ [self.text hash];
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
}
// valuenilkeynil
- (void)setNilValueForKey:(NSString *)key {
}
// 使 valueForKey key
- (id)valueForUndefinedKey:(NSString *)key {
return nil;
}
@end
///
@implementation NSArray (BRPickerView)
///
+ (NSArray *)br_modelArrayWithJson:(NSArray *)dataArr mapper:(nullable NSDictionary *)mapper {
if (!dataArr || dataArr.count == 0) {
return nil;
}
if (!mapper) {
// 使
mapper = @{
@"code": @"code",
@"text": @"text",
@"parentCode": @"parent_code",
@"extras": @"extras",
@"children": @"children"
};
}
NSMutableArray *tempArr = [NSMutableArray array];
for (NSDictionary *dic in dataArr) {
BRTextModel *model = [[BRTextModel alloc]init];
NSString *codeMappingKey = mapper[@"code"] ?: @"code";
model.code = dic[codeMappingKey] ? [NSString stringWithFormat:@"%@", dic[codeMappingKey]] : nil;
NSString *textMappingKey = mapper[@"text"] ?: @"text";
model.text = dic[textMappingKey];
NSString *parentCodeMappingKey = mapper[@"parentCode"] ?: @"parentCode";
model.parentCode = dic[parentCodeMappingKey] ? [NSString stringWithFormat:@"%@", dic[parentCodeMappingKey]] : nil;
NSString *extrasMappingKey = mapper[@"extras"] ?: @"extras";
model.extras = dic[extrasMappingKey];
NSString *childrenMappingKey = mapper[@"children"] ?: @"children";
NSArray *children = dic[childrenMappingKey];
if (children && children.count > 0) {
model.children = [self br_modelArrayWithJson:children mapper:mapper]; // list
}
[tempArr addObject:model];
}
return [tempArr copy];
}
///
- (NSArray *)br_getValueArr:(NSString *)propertyName {
NSMutableArray *valueArr = [[NSMutableArray alloc]init];
for (BRTextModel *model in self) {
id propertyValue = [model valueForKey:propertyName];
if (propertyValue) {
[valueArr addObject:propertyValue];
}
}
return [valueArr copy];
}
///
- (NSString *)br_joinValue:(NSString *)propertyName separator:(NSString *)separator {
NSArray *valueArr = [self br_getValueArr:propertyName];
if (valueArr && valueArr.count > 0) {
return [valueArr componentsJoinedByString:separator];
}
return @"";
}
/// text
- (NSString *)br_joinText:(NSString *)separator {
NSArray *valueArr = [self br_getValueArr:@"text"];
if (valueArr && valueArr.count > 0) {
return [valueArr componentsJoinedByString:separator];
}
return @"";
}
///
- (NSArray<BRTextModel *> *)br_buildTreeArray {
NSMutableArray<BRTextModel *> *treeModels = [NSMutableArray array];
NSMutableDictionary<NSString *, BRTextModel *> *allItemDic = [NSMutableDictionary dictionary];
// code key
for (BRTextModel *model in self) {
if (model.code && model.code.length > 0) {
allItemDic[model.code] = model;
}
}
for (BRTextModel *model in self) {
NSString *parentCode = model.parentCode;
BRTextModel *parentModel = parentCode && parentCode.length > 0 ? allItemDic[parentCode] : nil;
if (parentModel) {
if (!parentModel.children) {
parentModel.children = [NSArray array];
}
parentModel.children = [parentModel.children arrayByAddingObject:model];
} else {
//
[treeModels addObject:model];
}
}
return treeModels;
}
@end

View File

@ -0,0 +1,172 @@
//
// BRTextPickerView.h
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import "BRPickerAlertView.h"
#import "BRTextModel.h"
NS_ASSUME_NONNULL_BEGIN
/// 文本选择器类型
typedef NS_ENUM(NSInteger, BRTextPickerMode) {
/** 单列选择器 */
BRTextPickerComponentSingle,
/** 多列选择器 */
BRTextPickerComponentMulti,
/** 多列联动选择器 */
BRTextPickerComponentCascade
};
typedef void(^BRSingleResultBlock)(BRTextModel * _Nullable model, NSInteger index);
typedef void(^BRMultiResultBlock)(NSArray <BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs);
@interface BRTextPickerView : BRPickerAlertView
/**
////////////////////////////////////////////////////////////////////////// *
///
/// 【用法一】
/// 特点:灵活,扩展性强(推荐使用!)
///
////////////////////////////////////////////////////////////////////////*/
/** 文本选择器显示类型 */
@property (nonatomic, assign) BRTextPickerMode pickerMode;
/**
* 1.
* @[@"", @"", @"其他"](NSArray <BRTextModel *>*)
* @[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]]
*
* (NSArray <BRTextModel *>*) JSON
[
{
"text" : "北京市",
"children" : [
{ "text": "北京城区", "children": [{ "text": "东城区" }, { "text": "西城区" }] }
]
},
{
"text" : "浙江省",
"children" : [
{ "text": "杭州市", "children": [{ "text": "西湖区" }, { "text": "滨江区" }] },
{ "text": "宁波市", "children": [{ "text": "海曙区" }, { "text": "江北区" }] }
]
}
]
使 JSON
dataSourceArr = [NSArray br_modelArrayWithJson:dataArr mapper:nil];
*/
@property (nullable, nonatomic, copy) NSArray *dataSourceArr;
/**
* 2. plist/json
* / plist @"education_data.plist"
* JSON @"region_tree_data.json"JSON数据源的格式
*
* /JSON格式数据 plist/json
*/
@property (nullable, nonatomic, copy) NSString *fileName;
/** 设置默认选中的位置【单列】*/
@property (nonatomic, assign) NSInteger selectIndex;
/** 设置默认选中的位置【多列】*/
@property (nullable, nonatomic, copy) NSArray <NSNumber *> *selectIndexs;
/** 滚动选择时,触发的回调【单列】 */
@property (nullable, nonatomic, copy) BRSingleResultBlock singleChangeBlock;
/** 滚动选择时,触发的回调【多列】 */
@property (nullable, nonatomic, copy) BRMultiResultBlock multiChangeBlock;
/** 点击确定时,触发的回调【单列】 */
@property (nullable, nonatomic, copy) BRSingleResultBlock singleResultBlock;
/** 点击确定时,触发的回调【多列】 */
@property (nullable, nonatomic, copy) BRMultiResultBlock multiResultBlock;
/** 判断选择器是否处于滚动中。可以用于解决快速滑动选择器,在滚动结束前秒选确定按钮,出现显示不对的问题 */
@property (nonatomic, readonly, assign, getter=isRolling) BOOL rolling;
/** 设置选择器显示的列数(即层级数),默认是根据数据源层级动态计算显示 */
@property (nonatomic, assign) NSUInteger showColumnNum;
/** 滚动至选择行动画,默认为 NO */
@property (nonatomic, assign) BOOL selectRowAnimated;
/// 初始化文本选择器
/// @param pickerMode 文本选择器显示类型
- (instancetype)initWithPickerMode:(BRTextPickerMode)pickerMode;
/// 弹出选择器视图
- (void)show;
/// 关闭选择器视图
- (void)dismiss;
//================================================= 华丽的分割线 =================================================
/**
//////////////////////////////////////////////////////////////////////////
///
/// 【用法二】:快捷使用,直接选择下面其中的一个方法进行使用
/// 特点:快捷,方便
///
////////////////////////////////////////////////////////////////////////*/
/**
* 1.
*
* @param title
* @param dataSourceArr @[@"", @"", @"其他"]
* @param selectIndex
* @param resultBlock
*
*/
+ (void)showPickerWithTitle:(nullable NSString *)title
dataSourceArr:(nullable NSArray *)dataSourceArr
selectIndex:(NSInteger)selectIndex
resultBlock:(nullable BRSingleResultBlock)resultBlock;
/**
* 2.
*
* @param title
* @param dataSourceArr @[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]]
* @param selectIndexs @[@2, @1]
* @param resultBlock
*
*/
+ (void)showMultiPickerWithTitle:(nullable NSString *)title
dataSourceArr:(nullable NSArray *)dataSourceArr
selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
resultBlock:(nullable BRMultiResultBlock)resultBlock;
/**
* 3.
*
* @param title
* @param dataSourceArr (NSArray <BRTextModel *>*)
* @param selectIndexs @[@2, @1]
* @param resultBlock
*
*/
+ (void)showCascadePickerWithTitle:(nullable NSString *)title
dataSourceArr:(nullable NSArray *)dataSourceArr
selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
resultBlock:(nullable BRMultiResultBlock)resultBlock;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,623 @@
//
// BRTextPickerView.m
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// https://github.com/agiapp/BRPickerView
#import "BRTextPickerView.h"
@interface BRTextPickerView ()<UIPickerViewDelegate, UIPickerViewDataSource>
{
BOOL _isSyncingRollingSelection; // change
}
/** */
@property (nonatomic, strong) UIPickerView *pickerView;
/** */
@property (nonatomic, copy) NSArray *dataList;
@end
@implementation BRTextPickerView
#pragma mark - 1.
+ (void)showPickerWithTitle:(NSString *)title
dataSourceArr:(NSArray *)dataSourceArr
selectIndex:(NSInteger)selectIndex
resultBlock:(BRSingleResultBlock)resultBlock {
//
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentSingle;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndex = selectIndex;
strPickerView.singleResultBlock = resultBlock;
//
[strPickerView show];
}
#pragma mark - 2.
+ (void)showMultiPickerWithTitle:(NSString *)title
dataSourceArr:(NSArray *)dataSourceArr
selectIndexs:(NSArray <NSNumber *>*)selectIndexs
resultBlock:(BRMultiResultBlock)resultBlock {
//
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentMulti;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndexs = selectIndexs;
strPickerView.multiResultBlock = resultBlock;
//
[strPickerView show];
}
#pragma mark - 3.
+ (void)showCascadePickerWithTitle:(nullable NSString *)title
dataSourceArr:(nullable NSArray *)dataSourceArr
selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
resultBlock:(nullable BRMultiResultBlock)resultBlock {
//
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentCascade;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndexs = selectIndexs;
strPickerView.multiResultBlock = resultBlock;
//
[strPickerView show];
}
#pragma mark -
- (instancetype)initWithPickerMode:(BRTextPickerMode)pickerMode {
if (self = [super init]) {
self.pickerMode = pickerMode;
}
return self;
}
#pragma mark -
- (void)handlerPickerData {
// 1.
BOOL dataSourceError = NO;
if (self.dataSourceArr.count == 0) {
dataSourceError = YES;
}
id item = [self.dataSourceArr firstObject];
if (self.pickerMode == BRTextPickerComponentSingle) {
dataSourceError = !([item isKindOfClass:[NSString class]] || [item isKindOfClass:[BRTextModel class]]);
} else if (self.pickerMode == BRTextPickerComponentMulti) {
dataSourceError = ![item isKindOfClass:[NSArray class]];
} else if (self.pickerMode == BRTextPickerComponentCascade) {
dataSourceError = ![item isKindOfClass:[BRTextModel class]];
}
if (dataSourceError) {
BRErrorLog(@"数据源异常!请检查选择器数据源的格式");
return;
}
// 2.
if (self.pickerMode == BRTextPickerComponentSingle) {
self.dataList = self.dataSourceArr;
if (self.selectIndex < 0 || self.selectIndex >= self.dataList.count) {
self.selectIndex = 0;
}
} else if (self.pickerMode == BRTextPickerComponentMulti) {
self.dataList = self.dataSourceArr;
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
for (NSInteger component = 0; component < self.dataList.count; component++) {
NSArray *itemArr = self.dataList[component];
NSInteger row = 0;
if (self.selectIndexs.count > 0 && component < self.selectIndexs.count) {
NSInteger index = [self.selectIndexs[component] integerValue];
row = (index >= 0 && index < itemArr.count) ? index : 0;
}
[selectIndexs addObject:@(row)];
}
self.selectIndexs = [selectIndexs copy];
} else if (self.pickerMode == BRTextPickerComponentCascade) {
NSMutableArray *dataList = [[NSMutableArray alloc]init];
[dataList addObject:self.dataSourceArr];
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
BOOL hasNext = self.dataSourceArr.count > 0;
NSInteger i = 0;
NSInteger selectIndex = self.selectIndexs.count > 0 && i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[selectIndexs addObject:@(selectIndex)];
if (selectIndex >= 0 && selectIndex < self.dataSourceArr.count) {
BRTextModel *selectModel = self.dataSourceArr[selectIndex];
while (hasNext) {
NSArray *nextArr = selectModel.children;
if (!nextArr || nextArr.count == 0) {
hasNext = NO;
break;
}
[dataList addObject:nextArr];
i++;
selectIndex = self.selectIndexs.count > 0 && i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[selectIndexs addObject:@(selectIndex)];
if (selectIndex < nextArr.count) {
selectModel = nextArr[selectIndex];
}
}
}
//
if (self.showColumnNum > 0) {
NSInteger dataListCount = dataList.count;
if (self.showColumnNum < dataListCount) {
//
dataList = [[dataList subarrayWithRange:NSMakeRange(0, self.showColumnNum)] mutableCopy];
selectIndexs = [[selectIndexs subarrayWithRange:NSMakeRange(0, self.showColumnNum)] mutableCopy];
} else {
//
for (NSInteger i = 0; i < self.showColumnNum - dataListCount; i++) {
//
BRTextModel *placeholderModel = [[BRTextModel alloc]init];
NSArray *placeholderArr = @[placeholderModel];
[dataList addObject:placeholderArr];
[selectIndexs addObject:@(0)];
}
}
}
self.dataList = [dataList copy];
self.selectIndexs = [selectIndexs copy];
}
}
#pragma mark -
- (UIPickerView *)pickerView {
if (!_pickerView) {
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
_pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight)];
_pickerView.backgroundColor = self.pickerStyle.pickerColor;
_pickerView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
_pickerView.dataSource = self;
_pickerView.delegate = self;
}
return _pickerView;
}
#pragma mark - UIPickerViewDataSource
// 1.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
return 1;
case BRTextPickerComponentMulti:
case BRTextPickerComponentCascade:
{
if (self.pickerStyle.columnSpacing > 0) {
return self.dataList.count * 2 - 1;
}
return self.dataList.count;
}
default:
break;
}
}
// 2.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
return self.dataList.count;
case BRTextPickerComponentMulti:
case BRTextPickerComponentCascade:
{
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return 1;
} else {
component = component / 2;
}
}
if (component < self.dataList.count) {
return [self.dataList[component] count];
}
return 0;
}
break;
default:
break;
}
}
#pragma mark - UIPickerViewDelegate
// 3. pickerView
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
// 1. row
UILabel *label = (UILabel *)view;
if (!label) {
label = [[UILabel alloc]init];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = self.pickerStyle.pickerTextFont;
label.textColor = self.pickerStyle.pickerTextColor;
label.numberOfLines = self.pickerStyle.maxTextLines;
//
label.adjustsFontSizeToFitWidth = YES;
//
label.minimumScaleFactor = 0.5f;
}
// 2.
[self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component];
//
if (self.pickerMode == BRTextPickerComponentSingle) {
id item = row < self.dataList.count ? self.dataList[row] : nil;
if (item && [item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
label.text = model.text;
} else {
label.text = item;
}
} else if (self.pickerMode == BRTextPickerComponentMulti || self.pickerMode == BRTextPickerComponentCascade) {
//
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
label.text = @"";
return label;
} else {
component = component / 2;
}
}
if (component < self.dataList.count) {
NSArray *itemArr = self.dataList[component];
id item = row < itemArr.count ? itemArr[row] : nil;
if (item && [item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
label.text = model.text;
} else {
label.text = item;
}
}
}
return label;
}
//
- (BOOL)getRollingStatus:(UIView *)view {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)view;
if (scrollView.dragging || scrollView.decelerating) {
// UIPickerView YES
return YES;
}
}
for (UIView *subView in view.subviews) {
if ([self getRollingStatus:subView]) {
return YES;
}
}
return NO;
}
//
- (BOOL)isRolling {
return [self getRollingStatus:self.pickerView];
}
// 4. pickerView
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
{
self.selectIndex = row;
if (!_isSyncingRollingSelection) {
// singleChangeBlock
self.singleChangeBlock ? self.singleChangeBlock([self getSingleSelectModel], self.selectIndex): nil;
}
}
break;
case BRTextPickerComponentMulti:
{
//
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return;
} else {
component = component / 2;
}
}
NSMutableArray *mutableArr = [NSMutableArray arrayWithArray:self.selectIndexs ?: @[]];
while (mutableArr.count <= component) {
[mutableArr addObject:@(0)];
}
[mutableArr replaceObjectAtIndex:component withObject:@(row)];
self.selectIndexs = [mutableArr copy];
if (!_isSyncingRollingSelection) {
// multiChangeBlock
self.multiChangeBlock ? self.multiChangeBlock([self getMultiSelectModels], self.selectIndexs): nil;
}
}
break;
case BRTextPickerComponentCascade:
{
//
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return;
} else {
component = component / 2;
}
}
if (component < self.selectIndexs.count) {
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
if (i < component) {
[selectIndexs addObject:self.selectIndexs[i]];
} else if (i == component) {
[selectIndexs addObject:@(row)];
} else {
[selectIndexs addObject:@(0)];
}
}
self.selectIndexs = [selectIndexs copy];
}
if (!_isSyncingRollingSelection) {
//
[self reloadData];
// multiChangeBlock
self.multiChangeBlock ? self.multiChangeBlock([self getMultiSelectModels], self.selectIndexs): nil;
}
}
break;
default:
break;
}
}
#pragma mark -
- (BRTextModel *)getSingleSelectModel {
id item = self.selectIndex < self.dataList.count ? self.dataList[self.selectIndex] : nil;
if ([item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
model.index = self.selectIndex;
return model;
} else {
BRTextModel *model = [[BRTextModel alloc]init];
model.index = self.selectIndex;
model.text = item;
return model;
}
}
#pragma mark -
- (NSArray *)getMultiSelectModels {
NSMutableArray *modelArr = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < self.dataList.count; i++) {
NSInteger index = i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
NSArray *dataArr = i < self.dataList.count ? self.dataList[i] : @[];
id item = index >= 0 && index < dataArr.count ? dataArr[index] : nil;
if ([item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
model.index = index;
[modelArr addObject:model];
} else {
BRTextModel *model = [[BRTextModel alloc]init];
model.index = index;
model.text = item;
[modelArr addObject:model];
}
}
return [modelArr copy];
}
//
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return self.pickerStyle.rowHeight;
}
//
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
if (self.pickerStyle.columnSpacing > 0 && component % 2 == 1) {
return self.pickerStyle.columnSpacing;
}
NSInteger columnCount = [self numberOfComponentsInPickerView:pickerView];
CGFloat columnWidth = self.pickerView.bounds.size.width / columnCount - 5;
if (self.pickerStyle.columnWidth > 0 && self.pickerStyle.columnWidth <= columnWidth) {
return self.pickerStyle.columnWidth;
}
return columnWidth;
}
#pragma mark -
- (void)reloadData {
// 1.
[self handlerPickerData];
// 2.
[self.pickerView reloadAllComponents];
// 3.
if (self.pickerMode == BRTextPickerComponentSingle) {
if (self.selectIndex >= 0 && self.selectIndex < self.dataList.count) {
[self.pickerView selectRow:self.selectIndex inComponent:0 animated:self.selectRowAnimated];
}
} else if (self.pickerMode == BRTextPickerComponentMulti || self.pickerMode == BRTextPickerComponentCascade) {
for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
NSInteger component = self.pickerStyle.columnSpacing > 0 ? i * 2 : i;
NSInteger row = i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[self.pickerView selectRow:row inComponent:component animated:self.selectRowAnimated];
}
}
}
- (void)addPickerToView:(UIView *)view {
// 1.
if (view) {
// view view 使
[view setNeedsLayout];
[view layoutIfNeeded];
self.frame = view.bounds;
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
self.pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
[self addSubview:self.pickerView];
} else {
// iOS16 pickerView frame
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
self.pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
[self.alertView addSubview:self.pickerView];
}
// 线
if (self.pickerStyle.clearPickerNewStyle) {
[self.pickerStyle addSeparatorLineView:self.pickerView];
}
// 2.
[self reloadData];
__weak typeof(self) weakSelf = self;
// block
self.doneBlock = ^{
if (!weakSelf) {
return;
}
if (weakSelf.isRolling) {
[weakSelf handleAutoSelectRollingRow];
}
//
if (weakSelf.pickerMode == BRTextPickerComponentSingle) {
weakSelf.singleResultBlock ? weakSelf.singleResultBlock([weakSelf getSingleSelectModel], weakSelf.selectIndex): nil;
} else if (weakSelf.pickerMode == BRTextPickerComponentMulti || weakSelf.pickerMode == BRTextPickerComponentCascade) {
weakSelf.multiResultBlock ? weakSelf.multiResultBlock([weakSelf getMultiSelectModels], weakSelf.selectIndexs): nil;
}
};
[super addPickerToView:view];
}
#pragma mark -
- (NSInteger)clampedSelectedRowInComponent:(NSInteger)component {
NSInteger maxRow = [_pickerView numberOfRowsInComponent:component] - 1;
if (maxRow < 0) {
return NSNotFound;
}
NSInteger row = [_pickerView selectedRowInComponent:component];
return MAX(0, MIN(row, maxRow));
}
#pragma mark -
- (void)handleAutoSelectRollingRow {
if (!_pickerView) {
return;
}
NSInteger componentCount = [_pickerView numberOfComponents];
if (componentCount <= 0) {
return;
}
if (self.pickerMode == BRTextPickerComponentCascade) {
NSInteger changedIndex = NSNotFound;
NSInteger changedRow = NSNotFound;
for (NSInteger component = 0; component < componentCount; component++) {
if (self.pickerStyle.columnSpacing > 0 && component % 2 == 1) {
continue;
}
NSInteger dataComponent = self.pickerStyle.columnSpacing > 0 ? component / 2 : component;
NSInteger row = [self clampedSelectedRowInComponent:component];
if (row == NSNotFound) {
continue;
}
NSInteger oldRow = dataComponent < self.selectIndexs.count ? [self.selectIndexs[dataComponent] integerValue] : 0;
if (row != oldRow) {
changedIndex = dataComponent;
changedRow = row;
break;
}
}
if (changedIndex != NSNotFound) {
NSMutableArray<NSNumber *> *selectIndexs = [NSMutableArray array];
for (NSInteger index = 0; index < self.selectIndexs.count; index++) {
if (index < changedIndex) {
[selectIndexs addObject:self.selectIndexs[index]];
} else if (index == changedIndex) {
[selectIndexs addObject:@(changedRow)];
} else {
[selectIndexs addObject:@(0)];
}
}
self.selectIndexs = [selectIndexs copy];
[self reloadData];
}
return;
}
_isSyncingRollingSelection = YES;
if (self.pickerMode == BRTextPickerComponentSingle) {
NSInteger row = [self clampedSelectedRowInComponent:0];
if (row != NSNotFound) {
[self pickerView:_pickerView didSelectRow:row inComponent:0];
}
} else {
for (NSInteger component = 0; component < componentCount; component++) {
NSInteger row = [self clampedSelectedRowInComponent:component];
if (row == NSNotFound) {
continue;
}
[self pickerView:_pickerView didSelectRow:row inComponent:component];
}
}
_isSyncingRollingSelection = NO;
}
#pragma mark -
- (void)addSubViewToPicker:(UIView *)customView {
[self.pickerView addSubview:customView];
}
#pragma mark -
- (void)show {
[self addPickerToView:nil];
}
#pragma mark -
- (void)dismiss {
[self removePickerFromView:nil];
}
#pragma mark - setter
- (void)setFileName:(NSString *)fileName {
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
if (filePath && filePath.length > 0) {
if ([fileName hasSuffix:@".plist"]) {
// plist
NSArray *dataArr = [[NSArray alloc] initWithContentsOfFile:filePath];
if (dataArr && dataArr.count > 0) {
self.dataSourceArr = dataArr;
}
} else if ([fileName hasSuffix:@".json"]) {
// JSON
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSArray *dataArr = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
if (dataArr && dataArr.count > 0) {
self.dataSourceArr = [NSArray br_modelArrayWithJson:dataArr mapper:nil];
}
}
}
}
@end

21
Pods/BRPickerView/LICENSE generated Executable file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 91renb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

926
Pods/BRPickerView/README.md generated Executable file
View File

@ -0,0 +1,926 @@
# BRPickerView
BRPickerView 封装的是iOS中常用的选择器组件主要包括**`BRDatePickerView`** 日期选择器支持年月日、年月等15种日期样式选择支持设置星期、至今等、**`BRTextPickerView`** 文本选择器(支持单列、多列、省市区、省市、省、自定义多级联动选择)。支持自定义主题样式,适配深色模式,支持将选择器组件添加到指定容器视图。
⚠️【特别说明】
>- 从 `V3.0.0` 版本起,移除了 `BRAddressPickerView`、`BRStringPickerView` 废弃组件;请使用 `BRTextPickerView` 新组件进行替代。如果项目大面积报错:① 可暂时使用 2.9.9 旧版本:`pod 'BRPickerView', '2.9.9'`;② 或下载Demo手动把`Deprecated` 目录添加到项目中。
>- 如果不能找到最新版本,请先执行一下 `pod repo update ` 更新本地仓库,使 CocoaPods 能识别最新可用的库版本。
#### 📒 稀土掘金https://juejin.cn/post/6844903605468676104
# 效果演示
查看并运行 `BRPickerViewDemo.xcodeproj`
| ![效果图1](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/a.gif?raw=true) | ![效果图2](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/b.gif?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| 框架Demo运行效果图1 | 框架Demo运行效果图2 |
# 安装
#### CocoaPods
1. 在 Podfile 中添加 `pod 'BRPickerView'`
2. 执行 `pod install``pod update`
3. 导入头文件 ` #import <BRPickerView.h>`
#### SPM Supported
1. 依次点击 Xcode 的菜单 File > Add Package Dependencies...
2. 输出 `https://github.com/agiapp/BRPickerView`搜索并选择,然后点击 Add Package
#### 手动导入
1. 将与 `README.md` 同级目录下的 `BRPickerView` 文件夹拽入项目中注意删除PrivacyInfo.xcprivacy文件
2. 导入头文件 ` #import "BRPickerView.h"`
# 系统要求
- iOS 11.0+
- ARC
# 使用
### 时间选择器:`BRDatePickerView`
查看 BRDatePickerView.h 头文件,里面提供了两种使用方式,参见源码。
```objective-c
/// 日期选择器格式
typedef NS_ENUM(NSInteger, BRDatePickerMode) {
// ----- 以下4种是系统样式兼容国际化日期格式 -----
/** 【yyyy-MM-dd】UIDatePickerModeDate美式日期MM-dd-yyyy英式日期dd-MM-yyyy*/
BRDatePickerModeDate,
/** 【yyyy-MM-dd HH:mm】 UIDatePickerModeDateAndTime */
BRDatePickerModeDateAndTime,
/** 【HH:mm】UIDatePickerModeTime */
BRDatePickerModeTime,
/** 【HH:mm】UIDatePickerModeCountDownTimer */
BRDatePickerModeCountDownTimer,
// ----- 以下14种是自定义样式 -----
/** 【yyyy-MM-dd HH:mm:ss】年月日时分秒 */
BRDatePickerModeYMDHMS,
/** 【yyyy-MM-dd HH:mm】年月日时分 */
BRDatePickerModeYMDHM,
/** 【yyyy-MM-dd HH】年月日时 */
BRDatePickerModeYMDH,
/** 【MM-dd HH:mm】月日时分 */
BRDatePickerModeMDHM,
/** 【yyyy-MM-dd】年月日兼容国际化日期dd-MM-yyyy*/
BRDatePickerModeYMD,
/** 【yyyy-MM】年月兼容国际化日期MM-yyyy*/
BRDatePickerModeYM,
/** 【yyyy】年 */
BRDatePickerModeY,
/** 【MM-dd】月日 */
BRDatePickerModeMD,
/** 【HH:mm:ss】时分秒 */
BRDatePickerModeHMS,
/** 【HH:mm】时分 */
BRDatePickerModeHM,
/** 【mm:ss】分秒 */
BRDatePickerModeMS,
/** 【yyyy-qq】年季度 */
BRDatePickerModeYQ,
/** 【yyyy-MM-ww】年月周 */
BRDatePickerModeYMW,
/** 【yyyy-ww】年周 */
BRDatePickerModeYW
};
```
- 使用示例参考Demo
```objective-c
// 1.创建日期选择器
BRDatePickerView *datePickerView = [[BRDatePickerView alloc]init];
// 2.设置属性
datePickerView.pickerMode = BRDatePickerModeYMD;
datePickerView.title = @"选择年月日";
// datePickerView.selectValue = @"2019-10-30";
datePickerView.selectDate = [NSDate br_setYear:2019 month:10 day:30];
datePickerView.minDate = [NSDate br_setYear:1949 month:3 day:12];
datePickerView.maxDate = [NSDate date];
datePickerView.isAutoSelect = YES;
datePickerView.resultBlock = ^(NSDate *selectDate, NSString *selectValue) {
NSLog(@"选择的值:%@", selectValue);
};
// 设置自定义样式
BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
customStyle.pickerColor = BR_RGB_HEX(0xd9dbdf, 1.0f);
customStyle.pickerTextColor = [UIColor redColor];
customStyle.separatorColor = [UIColor redColor];
datePickerView.pickerStyle = customStyle;
// 3.显示
[datePickerView show];
```
**时间选择器显示类型的效果图(默认样式):**
- 以下4种样式是使用 UIDatePicker 类 进行封装的,支持循环滚动
| ![样式1BRDatePickerModeTime](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type1.png?raw=true) | ![样式2BRDatePickerModeDate](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type2.png?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| 样式1BRDatePickerModeDate | 样式2BRDatePickerModeDateAndTime |
| | |
| ![样式3BRDatePickerModeDateAndTime](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type3.png?raw=true) | ![样式4BRDatePickerModeCountDownTimer](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type4.png?raw=true) |
| 样式3BRDatePickerModeTime | 样式4BRDatePickerModeCountDownTimer |
- 以下11种样式是使用 UIPickerView 类进行封装的。
| ![样式5BRDatePickerModeYMDHMS](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type5.png?raw=true) | ![样式6BRDatePickerModeYMDHM](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type6.png?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| 样式5BRDatePickerModeYMDHMS | 样式6BRDatePickerModeYMDHM |
| | |
| ![样式7BRDatePickerModeYMDH](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type7.png?raw=true) | ![样式8BRDatePickerModeMDHM](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type8.png?raw=true) |
| 样式7BRDatePickerModeYMDH | 样式8BRDatePickerModeMDHM |
| | |
| ![样式9BRDatePickerModeYMDE](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type9.png?raw=true) | ![样式10BRDatePickerModeYMD](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type10.png?raw=true) |
| 样式9BRDatePickerModeYMD | 样式10BRDatePickerModeYM |
| | |
| ![样式11BRDatePickerModeYM](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type11.png?raw=true) | ![样式12BRDatePickerModeY](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type12.png?raw=true) |
| 样式11BRDatePickerModeY | 样式12BRDatePickerModeMD |
| | |
| ![样式13BRDatePickerModeMD](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type13.png?raw=true) | ![样式14BRDatePickerModeHMS](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type14.png?raw=true) |
| 样式13BRDatePickerModeHMS | 样式14BRDatePickerModeHM |
| | |
| ![样式15BRDatePickerModeHM](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type15.png?raw=true) | |
| 样式15BRDatePickerModeMS | |
- 其它日期样式
| ![设置显示星期](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_week1.png?raw=true) | ![设置显示星期](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_week2.png?raw=true) |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 设置显示星期datePickerView.showWeek = YES; | 设置显示星期datePickerView.showWeek = YES; |
| | |
| ![设置添加至今](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_now.png?raw=true) | ![设置显示今天](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_today.png?raw=true) |
| 设置添加至今datePickerView.addToNow = YES; | 设置显示今天datePickerView.showToday = YES; |
| | |
| ![日期单位单行显示样式](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_unit.png?raw=true) | ![自定义选择器选中行颜色](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_row.png?raw=true) |
| 日期单位显示样式datePickerView.showUnitType = BRShowUnitTypeOnlyCenter; | 设置选择器中间选中行的背景颜色selectRowColor |
```objective-c
// 设置选择器中间选中行的样式
BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
customStyle.selectRowColor = [UIColor blueColor];
customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
customStyle.selectRowTextColor = [UIColor redColor];
datePickerView.pickerStyle = customStyle;
```
| ![英式日期年月日](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_en1.png?raw=true) | ![英式日期年月](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_en2.png?raw=true) |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 样式BRDatePickerModeYMD (默认非中文环境显示英式日期) | 样式BRDatePickerModeYM (默认非中文环境显示英式日期) |
- 几种常见的弹框样式模板
| ![模板样式1](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/template_style1.png?raw=true) | ![模板样式2](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/template_style2.png?raw=true) |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 弹框样式模板1datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithThemeColor:[UIColor blueColor]]; | 弹框样式模板2datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithDoneTextColor:[UIColor blueColor]]; |
| | |
| ![模板样式3](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/template_style3.png?raw=true) | ![添加选择器的头视图](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_top.png?raw=true) |
| 弹框样式模板3datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithDoneBtnImage:[UIImage imageNamed:@"icon_close"]]; | 添加选择器的头视图pickerHeaderView |
```objective-c
// 添加选择器头视图pickerHeaderView
UIView *headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 36)];
headerView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.1f];
NSArray *unitArr = @[@"年", @"月", @"日"];
for (NSInteger i = 0; i < unitArr.count; i++) {
CGFloat width = SCREEN_WIDTH / unitArr.count;
CGFloat orginX = i * (SCREEN_WIDTH / unitArr.count);
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(orginX, 0, width, 36)];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:16.0f];
label.textColor = [UIColor darkGrayColor];
label.text = unitArr[i];
[headerView addSubview:label];
}
datePickerView.pickerHeaderView = headerView;
```
### 文本选择器:`BRTextPickerView`
查看 BRTextPickerView.h 头文件,提供了三种类型:
```objective-c
/// 文本选择器类型
typedef NS_ENUM(NSInteger, BRTextPickerMode) {
/** 单列选择器 */
BRTextPickerComponentSingle,
/** 多列选择器 */
BRTextPickerComponentMulti,
/** 多列联动选择器 */
BRTextPickerComponentCascade
};
```
#### 1. 单列文本选择器
- 使用示例:
```objective-c
/// 单列文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentSingle];
textPickerView.title = @"学历";
// 设置数据源
textPickerView.dataSourceArr = @[@"大专以下", @"大专", @"本科", @"硕士", @"博士", @"博士后"];
textPickerView.selectIndex = self.mySelectIndex;
textPickerView.singleResultBlock = ^(BRTextModel * _Nullable model, NSInteger index) {
NSLog(@"选择的值:%@", model.text);
self.mySelectIndex = index;
textField.text = model.text;
};
[textPickerView show];
```
- 设置数据源有3种方式
```objective-c
// 方式1传字符串数组
textPickerView.dataSourceArr = @[@"大专以下", @"大专", @"本科", @"硕士", @"博士", @"博士后"];
```
```objective-c
// 方式2直接传入 plist 文件名可以将上面的字符串数组放到本地plist文件中education_data.plist
textPickerView.fileName = @"education_data.plist";
```
```objective-c
// 方式3传入一维模型数组(NSArray <BRTextModel *>*)
NSArray *dataArr = @[@{@"code": @"1", @"text": @"大专以下"},
@{@"code": @"2", @"text": @"大专"},
@{@"code": @"3", @"text": @"本科"},
@{@"code": @"4", @"text": @"硕士"},
@{@"code": @"5", @"text": @"博士"},
@{@"code": @"6", @"text": @"博士后"}];
// 将上面数组 转为 模型数组(组件内封装的工具方法)
NSArray<BRTextModel *> *modelArr = [NSArray br_modelArrayWithJson:dataArr mapper:nil];
textPickerView.dataSourceArr = modelArr;
```
说明当字典key 与 BRTextModel模型的属性不匹配时需要指定模型属性与字典key的映射关系
```objective-c
/// 单列文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentSingle];
textPickerView.title = @"融资情况";
// 方式3传入一维模型数组(NSArray <BRTextModel *>*)
NSArray *dataArr = @[@{@"key": @"1001", @"value": @"无融资", @"remark": @""},
@{@"key": @"2001", @"value": @"天使轮", @"remark": @""},
@{@"key": @"3001", @"value": @"A轮", @"remark": @""},
@{@"key": @"4001", @"value": @"B轮", @"remark": @""},
@{@"key": @"5001", @"value": @"C轮以后", @"remark": @""},
@{@"key": @"6001", @"value": @"已上市", @"remark": @""}];
// 指定 BRTextModel模型的属性 与 字典key 的映射关系
NSDictionary *mapper = @{ @"code": @"key", @"text": @"value", @"extras": @"remark" };
// 将上面数组 转为 模型数组(组件内封装的工具方法)
NSArray<BRTextModel *> *modelArr = [NSArray br_modelArrayWithJson:dataArr mapper:mapper];
textPickerView.dataSourceArr = modelArr;
textPickerView.singleResultBlock = ^(BRTextModel * _Nullable model, NSInteger index) {
NSLog(@"选择的值:%@", model.text);
};
[textPickerView show];
```
- 单列文本选择器效果图:
| ![自定义单列字符串](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_single_xueli.png?raw=true) | ![融资情况](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_single_rongzi.png?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
#### 2. 多列文本选择器
- 使用示例:
```objective-c
/// 多列文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentMulti];
textPickerView.title = @"多列文本选择器";
// 设置数据源
textPickerView.dataSourceArr = @[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]];
textPickerView.selectIndexs = self.mySelectIndexs;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
self.mySelectIndexs = indexs;
// 将模型数组元素的 text 属性值,通过分隔符-连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@"-"];
NSLog(@"选择的结果:%@", selectText);
};
[textPickerView show];
```
- 多列文本选择器设置数据源同单列一样也有3种方式
```objective-c
// 方式1多维字符串数组
textPickerView.dataSourceArr = @[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]];
```
```objective-c
// 方式2直接传入 plist 文件名可以将上面的数组放到本地plist文件中grade_level_data.plist
textPickerView.fileName = @"grade_level_data.plist";
```
```objective-c
// 方式3传入多维模型数组
NSArray *subjectDataArr = @[@{@"subject_id": @"11", @"subject": @"语文"}, @{@"subject_id": @"12", @"subject": @"数学"}, @{@"subject_id": @"13", @"subject": @"英语"}];
NSArray *gradeDataArr = @[@{@"grade_id": @"1", @"grade": @"优秀"}, @{@"grade_id": @"2", @"grade": @"良好"}];
// 将上面数组 转为 模型数组(组件内封装的工具方法)
NSArray *subjectModelArr = [NSArray br_modelArrayWithJson:subjectDataArr mapper:@{ @"code": @"subject_id", @"text": @"subject" }];
NSArray *gradeModelArr = [NSArray br_modelArrayWithJson:gradeDataArr mapper:@{ @"code": @"grade_id", @"text": @"grade" }];
textPickerView.dataSourceArr = @[subjectModelArr, gradeModelArr];
```
- 另外还可以设置更多自定义样式
```objective-c
/// 多列文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentMulti];
textPickerView.title = @"自定义多列字符串";
textPickerView.dataSourceArr = @[@[@"01", @"02", @"03", @"04", @"05", @"06", @"07", @"08", @"09", @"10", @"11", @"12"], @[@"00", @"10", @"20", @"30", @"40", @"50"]];
textPickerView.selectIndexs = self.mySelectIndexs;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
self.mySelectIndexs = indexs;
// 将模型数组元素的 text 属性值,通过:分隔符 连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@":"];
NSLog(@"选择的结果:%@", selectText);
};
// 设置自定义样式
BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
// 设置 picker 的列宽
customStyle.columnWidth = 30;
// 设置 picker 的列间隔
customStyle.columnSpacing = 60;
// 设置圆角矩形背景
// 方式1使用系统自带样式保留iOS14之后系统默认的圆角样式。
customStyle.clearPickerNewStyle = NO;
// 方式2可以使用UIView自定义一个圆角矩形视图rectView并添加到 alertView 上也能实现同样的效果([textPickerView.alertView addSubview:rectView];
// 设置选择器中间选中行的样式
customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
customStyle.selectRowTextColor = [UIColor blueColor];
textPickerView.pickerStyle = customStyle;
[textPickerView show];
```
- 多列文本选择器效果图:
| ![多列文本选择器](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_multi_grade.png?raw=true) | ![多列文本选择器](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_multi_time.png?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
#### 3. 多列联动文本选择器
- 使用示例:
```objective-c
/// 多列联动文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentCascade];
textPickerView.title = @"多列联动文本选择器";
NSArray *dataArr = @[
@{
@"text" : @"浙江省",
@"children" : @[
@{ @"text": @"杭州市", @"children": @[@{ @"text": @"西湖区" }, @{ @"text": @"滨江区" }] },
@{ @"text": @"宁波市", @"children": @[@{ @"text": @"海曙区" }, @{ @"text": @"江北区" }] },
@{ @"text": @"温州市", @"children": @[@{ @"text": @"鹿城区" }, @{ @"text": @"龙湾区" }] }
]
},
@{
@"text" : @"江苏省",
@"children" : @[
@{ @"text": @"南京市", @"children": @[@{ @"text": @"玄武区" }, @{ @"text": @"秦淮区" }] },
@{ @"text": @"苏州市", @"children": @[@{ @"text": @"虎丘区" }, @{ @"text": @"吴中区" }] }
]
},
@{
@"text" : @"辽宁省",
@"children" : @[
@{ @"text": @"沈阳市", @"children": @[@{ @"text": @"沈河区" }, @{ @"text": @"和平区" }] },
@{ @"text": @"大连市", @"children": @[@{ @"text": @"中山区" }, @{ @"text": @"金州区" }] }
]
}
];
// 设置数据源:传树状结构模型数组(NSArray <BRTextModel *>*)
textPickerView.dataSourceArr = [NSArray br_modelArrayWithJson:dataArr mapper:nil];
textPickerView.selectIndexs = self.mySelectIndexs;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
self.mySelectIndexs = indexs;
// 将模型数组元素的 text 属性值,通过-分隔符 连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@"-"];
NSLog(@"选择的结果:%@", selectText);
};
[textPickerView show];
```
- 实现省、市、区/县选择(通常省市区/县数据是从自己业务后台API获取这里使用本地数据源仅做参考文件下载地址[region_tree_data.json](https://raw.githubusercontent.com/agiapp/BRPickerView/master/BRPickerViewDemo/DataFile/region_tree_data.json)
```objective-c
// 地区
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentCascade];
textPickerView.title = @"请选择地区";
// 设置数据源传入本地json文件名可以下载Demo中的 region_tree_data.json 文件放到自己的项目中)
textPickerView.fileName = @"region_tree_data.json";
// 设置选择器显示的列数(即层级数)默认是根据数据源层级动态计算显示。如设置1则只显示前1列数据即只显示省设置2则只显示前2列数据即只显示省、市设置3则只显示前3列数据即显示省、市、区
textPickerView.showColumnNum = 3;
textPickerView.selectIndexs = self.addressSelectIndexs;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
self.addressSelectIndexs = indexs;
// 将模型数组元素的 text 属性值,通过-分隔符 连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@"-"];
NSLog(@"选择的结果:%@", selectText);
textField.text = selectText;
};
[textPickerView show];
```
- 地址文本选择器的3种显示效果
| ![地区](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_cascade_area.png?raw=true) | ![城市](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_cascade_city.png?raw=true) |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 样式1textPickerView.showColumnNum = 3; | 样式2textPickerView.showColumnNum = 2; |
| | |
| ![省份](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_cascade_province.png?raw=true) | |
| 样式3textPickerView.showColumnNum = 1; | |
- 处理树状结构数据
```json
{
"code": "200",
"message": "OK",
"data": [
{
"adcode": "330000",
"name": "浙江省",
"districts" : [
{ "adcode" : "330100", "name": "杭州市", "districts": [{ "adcode" : "330106", "name": "西湖区" }, { "adcode" : "330108", "name": "滨江区" }] },
{ "adcode" : "330200", "name": "宁波市", "districts": [{ "adcode" : "330203", "name": "海曙区" }, { "adcode" : "330205", "name": "江北区" }] },
{ "adcode" : "330300", "name": "温州市", "districts": [{ "adcode" : "330302", "name": "鹿城区" }, { "adcode" : "330303", "name": "龙湾区" }] }
]
},
{
"adcode": "320000",
"name": "江苏省",
"districts" : [
{ "adcode" : "320100", "name": "南京市", "districts": [{ "adcode" : "320102", "name": "玄武区" }, { "adcode" : "320104", "name": "秦淮区" }] },
{ "adcode" : "320500", "name": "苏州市", "districts": [{ "adcode" : "320505", "name": "虎丘区" }, { "adcode" : "320506", "name": "吴中区" }] }
]
},
{
"adcode": "210000",
"name": "辽宁省",
"districts" : [
{ "adcode" : "210100", "name": "沈阳市", "districts": [{ "adcode" : "210103", "name": "沈河区" }, { "adcode" : "210102", "name": "和平区" }] },
{ "adcode" : "210200", "name": "大连市", "districts": [{ "adcode" : "210202", "name": "中山区" }, { "adcode" : "210213", "name": "金州区" }] }
]
}
]
}
```
```objective-c
/// 多列联动文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentCascade];
textPickerView.title = @"多列联动文本选择器";
// 接收网络请求结果数据(下面省略号表示省略部分代码)
NSArray *dataArr = ...... responseObject[@"data"];
// 指定 BRTextModel模型的属性 与 字典key 的映射关系
NSDictionary *mapper = @{ @"code": @"adcode", @"text": @"name", @"children": @"districts" };
// 将上面数组 转为 模型数组(组件内封装的工具方法)
NSArray<BRTextModel *> *modelArr = [NSArray br_modelArrayWithJson:dataArr mapper:mapper];
textPickerView.dataSourceArr = modelArr;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
// 将模型数组元素的 text 属性值,通过-分隔符 连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@"-"];
NSLog(@"选择的结果:%@", selectText);
};
[textPickerView show];
```
- 处理扁平结构数据
```json
{
"Code" : 200,
"Msg" : "获取成功",
"Result" : [
{
"ParentID" : "-1",
"ParentName" : "",
"CategoryID" : "330000",
"CategoryName" : "浙江省"
},
{
"ParentID" : "-1",
"ParentName" : "",
"CategoryID" : "320000",
"CategoryName" : "江苏省"
},
{
"ParentID" : "-1",
"ParentName" : "",
"CategoryID" : "210000",
"CategoryName" : "辽宁省"
},
{
"ParentID" : "330000",
"ParentName" : "浙江省",
"CategoryID" : "330100",
"CategoryName" : "杭州市"
},
{
"ParentID" : "330000",
"ParentName" : "浙江省",
"CategoryID" : "330200",
"CategoryName" : "宁波市"
},
{
"ParentID" : "330000",
"ParentName" : "浙江省",
"CategoryID" : "330300",
"CategoryName" : "温州市"
},
{
"ParentID" : "320000",
"ParentName" : "江苏省",
"CategoryID" : "320100",
"CategoryName" : "南京市"
},
{
"ParentID" : "320000",
"ParentName" : "江苏省",
"CategoryID" : "320500",
"CategoryName" : "苏州市"
},
{
"ParentID" : "210000",
"ParentName" : "辽宁省",
"CategoryID" : "210100",
"CategoryName" : "沈阳市"
},
{
"ParentID" : "210000",
"ParentName" : "辽宁省",
"CategoryID" : "210200",
"CategoryName" : "大连市"
}
]
}
```
```objective-c
/// 多列联动文本选择器
BRTextPickerView *textPickerView = [[BRTextPickerView alloc]initWithPickerMode:BRTextPickerComponentCascade];
textPickerView.title = @"多列联动文本选择器";
// 接收网络请求结果数据(下面省略号表示省略部分代码)
NSArray *dataArr = ...... responseObject[@"Result"];
// 指定 BRTextModel模型的属性 与 字典key 的映射关系
NSDictionary *mapper = @{ @"parentCode": @"ParentID", @"code": @"CategoryID", @"text": @"CategoryName" };
// 1.先将上面数组 转为 模型数组组件内封装的工具方法。如果数据源是多个数组需要自己先手动组装成一个NSArray<BRTextModel *>类型的扁平结构模型数组。
NSArray<BRTextModel *> *listModelArr = [NSArray br_modelArrayWithJson:dataArr mapper:mapper];
// 2.将扁平结构模型数组 转成 树状结构模型数组(组件内封装的工具方法)
NSArray<BRTextModel *> *treeModelArr = [listModelArr br_buildTreeArray];
textPickerView.dataSourceArr = treeModelArr;
textPickerView.multiResultBlock = ^(NSArray<BRTextModel *> * _Nullable models, NSArray<NSNumber *> * _Nullable indexs) {
// 将模型数组元素的 text 属性值,通过-分隔符 连接成字符串(组件内封装的工具方法)
NSString *selectText = [models br_joinText:@"-"];
NSLog(@"选择的结果:%@", selectText);
// 获取选择模型指定属性code组成的数组组件内封装的工具方法
NSArray *selectIDs = [models br_getValueArr:@"code"];
};
// 设置选择器中间选中行的样式
BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
customStyle.selectRowTextColor = [UIColor blueColor];
customStyle.columnWidth = 80;
customStyle.columnSpacing = 10;
textPickerView.pickerStyle = customStyle;
[textPickerView show];
```
- 多列联动文本选择器效果图:
| ![三列联动文本选择器](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_cascade_three.png?raw=true) | ![两列字符串选择器](https://github.com/agiapp/BRPickerView/blob/master/BRPickerViewDemo/images/text_cascade_two.png?raw=true) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
补充说明:对于一些需要特殊定制弹框的应用场景,可以使用下面的方法把日期选择器/文本选择器到指定容器视图上,实现个性化的弹框需求。
```objective-c
/// 扩展一:添加选择器到指定容器视图上
/// 应用场景:可将中间的滚轮选择器 pickerView 视图(不包含蒙层及标题栏)添加到任何自定义视图上(会自动填满容器视图),也方便自定义更多的弹框样式
/// 补充说明:如果是自定义确定按钮,需要回调默认选择的值:只需在自定义确定按钮的点击事件方法里执行一下 doneBlock 回调(目的是去触发组件内部执行 resultBlock 回调,进而回调默认选择的值)
/// @param view 容器视图
- (void)addPickerToView:(nullable UIView *)view;
```
# 更新记录
#### 2026-05-20V3.0.0
- 移除 BRAddressPickerView、BRStringPickerView 废弃组件
- 修改库支持的平台和版本为 iOS 11.0+
- fix[#355](https://github.com/agiapp/BRPickerView/issues/355)
#### 2025-10-24V2.9.9
- Merge pull request [#329](https://github.com/agiapp/BRPickerView/pull/329)、 [#348](https://github.com/agiapp/BRPickerView/pull/348)
#### 2025-10-17V2.9.8
- fix[#345](https://github.com/agiapp/BRPickerView/issues/345) 、[#349](https://github.com/agiapp/BRPickerView/issues/349)
#### 2025-05-27V2.9.7
- 修改 Swift Package Manager 集成方式
#### 2025-05-21V2.9.6
- fix[#318](https://github.com/agiapp/BRPickerView/issues/318)
#### 2025-04-22V2.9.5
- fix[#319](https://github.com/agiapp/BRPickerView/issues/319) 、[#326](https://github.com/agiapp/BRPickerView/issues/326) 、[#340](https://github.com/agiapp/BRPickerView/issues/340)
#### 2025-03-16V2.9.3
- [#336](https://github.com/agiapp/BRPickerView/issues/336) 优化选择年月日时分秒时UI显示最后一个秒显示不全问题
#### 2024-07-24V2.9.1
- 新增 maxTextLines 属性
- 取消 selectRowAnimated 属性 readonly 限制
#### 2024-07-17V2.9.0
- 新增 BRTextPickerView 文本选择组件用于替代BRAddressPickerView、BRStringPickerView组件
#### 2024-07-02V2.8.8
- [#310](https://github.com/agiapp/BRPickerView/issues/310) :更新本地省市区数据源数据
- [#314](https://github.com/agiapp/BRPickerView/issues/314) 修改maskView视图命名解决因命名冲突在iOS 18 上出现的崩溃问题
#### 2024-05-28V2.8.7
- 解决已知问题:[#308](https://github.com/agiapp/BRPickerView/issues/308) 、[#309](https://github.com/agiapp/BRPickerView/issues/309)
- 时间选择器新增 `twelveHourMode` 属性支持设置12小时制
- 支持 Swift Package Manager
#### 2024-04-28V2.8.5
- 解决已知问题:[#305](https://github.com/agiapp/BRPickerView/issues/305)
- 添加可设置选择器组件的列宽属性:`columnWidth`
- 添加可设置`BRStringPickerView` 选择器组件的列间隔属性:`columnSpacing`
#### 2024-04-23V2.8.2
- 解决已知问题:[#304](https://github.com/agiapp/BRPickerView/issues/304)
- Add PrivacyInfo.xcprivacy
#### 2022-07-08V2.8.0
- 优化代码。
#### 2022-06-16V2.7.8
- 优化代码。
#### 2022-03-30V2.7.7
- 优化代码。
#### 2021-10-09V2.7.6
- 适配iOS15
#### 2021-05-28V2.7.5
- 日期选择器新增属性:`monthNames` 和 `customUnit`
- 解决已知问题:[#232](https://github.com/agiapp/BRPickerView/issues/232) 、[#231](https://github.com/agiapp/BRPickerView/issues/231) 、[#230](https://github.com/agiapp/BRPickerView/issues/230) 、[#227](https://github.com/agiapp/BRPickerView/issues/227) 、[#225](https://github.com/agiapp/BRPickerView/issues/225) 、[#219](https://github.com/agiapp/BRPickerView/issues/219) 、[#206](https://github.com/agiapp/BRPickerView/issues/206)
#### 2020-09-25V2.7.3
- 适配选择器iOS14的样式[#189](https://github.com/agiapp/BRPickerView/issues/189) 、[#191](https://github.com/agiapp/BRPickerView/issues/191)
#### 2020-09-23V2.7.2
- 日期选择器新增添加自定义字符串属性:`firstRowContent` 和 `lastRowContent`
- 解决日期选择器设置最小日期时,存在的联动不正确的问题:[#184](https://github.com/agiapp/BRPickerView/issues/184)
#### 2020-08-28V2.7.0
- 日期选择器添加 `nonSelectableDates` 属性:[#178](https://github.com/agiapp/BRPickerView/issues/178)
- 优化选中行文本显示:[#177](https://github.com/agiapp/BRPickerView/issues/177)
#### 2020-08-16V2.6.8
- 优化代码,适配 iPad 分屏显示
- 新增 `keyView` 属性(即组件的父视图:可以将组件添加到 自己获取的 keyWindow 上,或页面的 view 上)
#### 2020-08-09V2.6.7
- 适配 iOS14
#### 2020-08-06V2.6.6
- 修复 [#163](https://github.com/agiapp/BRPickerView/issues/163) 和 [#170](https://github.com/agiapp/BRPickerView/issues/170)
#### 2020-07-18V2.6.5
- 字符串选择器新增支持多级联动选择
#### 2020-06-24V2.6.3
- 日期选择器新增属性:`timeZone` 和 `addCustomString`
#### 2020-05-12V2.6.2
- 实现 [#145](#145) 和 [#146](#146) 需求
#### 2020-04-30V2.6.0
- 新增样式属性:`selectRowTextColor` 和 `selectRowTextFont`
- 日期选择器新增数字显示属性:`numberFullName`
- 优化代码,添加 `BRDatePickerModeYMD` 支持国际化英式日期
- 修复 [#143](#143)
#### 2020-04-27V2.5.8
- 修复 [#138](https://github.com/agiapp/BRPickerView/issues/138) 和 [#142](https://github.com/agiapp/BRPickerView/issues/142)
- 日期选择器新增 `descending` 属性,支持降序的时间列表
- 更新地址选择器地区数据源
#### 2020-03-31V2.5.7
- 优化代码,解决已知问题
#### 2020-02-26V2.5.6
- 优化代码,兼容部分国际化日期样式
#### 2020-02-24V2.5.5
- 添加设置选择器选中行背景颜色的功能,新增属性 `selectRowColor`
#### 2020-01-31V2.5.3
- 新增属性:`pickerHeaderView`、`pickerFooterView`
- 新增刷新选择器数据方法:`reloadData`
#### 2020-01-05V2.5.1
- 优化代码,添加 `BRDatePickerModeYM` 支持国际化英式日期
#### 2020-01-02V2.5.0
- 日期选择器新增属性:`showUnitType`(日期单位显示样式)、`minuteInterval`、`secondInterval`
- 封装了常用的几种模板样式,使用更加简单便捷
- 框架内默认适配深色模式显示
#### 2019-12-26V2.4.6
- 添加支持动态更新属性 `title``selectDate`、`pickerMode` 的值
- 日期选择器添加 `showWeek` 属性,及新增 `BRDatePickerModeMS` 日期类型
- 优化选择器【用法二】的使用,新增选择器滚动选择时回调的属性
#### 2019-11-28V2.4.5
- 日期选择器新增选择 ”至今“ 和 显示 ”今天“ 的功能,见以下两个属性:
`showToday` :控制是否显示 “今天” ,默认为 NO
`addToNow`:控制是否添加选择 “至今”,默认为 NO
#### 2019-11-26V2.4.3
- 日期选择器新增以下三种选择类型:
`BRDatePickerModeYMDHMS`(年月日时分秒)、`BRDatePickerModeYMDE`(年月日星期)、`BRDatePickerModeHMS`(时分秒)
- 更新地址选择器地区数据源
#### 2019-11-07V2.4.2
- 日期选择器添加BRDatePickerModeYMDHyyyy-MM-dd HH类型
- 地址选择器添加selectIndexs 属性,可根据索引去设置默认选择
- 适配横屏及刘海屏安全区域显示效果
#### 2019-11-04V2.4.0
- 优化选择器子目录管理,方便轻量级、模块化集成
`pod 'BRPickerView'` // 集成全部的功能
`pod 'BRPickerView/DatePickerView'` // 仅集成日期选择器的功能
`pod 'BRPickerView/AddressPickerView'` // 仅集成地址选择器的功能
`pod 'BRPickerView/StringPickerView'` // 仅集成字符串选择器的功能
#### 2019-11-01V2.3.8
- 优化代码,添加更多的自定义样式属性
#### 2019-10-30V2.3.6
- 优化代码,添加国际化支持
#### 2019-10-26V2.3.5
- 添加传统的创建对象设置属性的使用方式
- 开放设置选择器颜色及样式,适配深色模式
- 更新省市区数据源,数据与政府官网最新公布的一致(参见:[行政区划代码](http://www.mca.gov.cn/article/sj/xzqh/2019/)
- 支持将选择器添加到指定容器视图上见BaseView.h文件扩展一方法
- 支持将子视图添加到选择器上见BaseView.h文件扩展二方法
- 优化代码配置Pod库的层级目录
#### 2018-04-27V2.2.1:
- 修复bug适配iPad和横屏显示。
- 优化代码,提高框架适应性,降低内存消耗。
#### 2018-04-03V2.2.0
- 时间选择器新添加了7种显示类型BRDatePickerMode可根据自己项目的需求选择性使用。
- 适配横屏,及 iPhoneX 底部安全区域。
- 修改了最小时间和最大时间的参数名称(以前版本是传 NSString 类型, 现在传 NSDate 类型)
- 修复比较时间大小时出现的bug。
#### 2018-03-19V2.1.3
- 修改地址选择器确认选择后的回调参数。
- 现修改如下:可通过省市区的模型获取省市区的 name名称、codeid、index索引`resultBlock:^(BRProvinceModel *province, BRCityModel *city, BRAreaModel *area) {}`
- 去掉第三方依赖库 `MJExtension` ,修改为手动解析地址数据源。
#### 2018-03-11V2.1.2
- 重命名了Github用户名更新项目相关的路径。提示pod之前的版本不受影响
#### 2018-02-28V2.1.1
- 修复某些情况下无法用bundle加载本地数据源BRCity.plistbug。
#### 2018-01-26V2.1.0
- 给地址选择器添加了一个方法见方法4提供数据源参数支持外部传入地区数据源。
- 提示:要注意数据源格式,参考 BRCity.json。可以把 BRCity.json 文件的内容放到后台去维护,通过后台接口获取地区数据源(即 BRCity.json 文件的内容)。
#### 2018-01-25V2.0.0
- 更新了地址数据源BRCity.plist地区信息是2018年最新最全的与微信的地区信息完全一致。
- 支持自定义默认选择地址(格式:@[@"浙江省", @"杭州市", @"西湖区"]),支持下次点击进入地址选择器时,默认地址为上次选择的结果。
- 修改了日期选择器、地址选择器、字符串选择器的接口方法删除了之前的方法2
- 添加了地址选择器显示类型支持3种显示只显示省份、显示省份和城市、显示省市区。
#### 2018-01-05V1.3.0
- 添加取消选择的回调方法(点击背景或取消按钮会执行 `cancelBlock`
- 合并了字符串选择器 数组数据源和plist数据源对应的方法`dataSource` 参数支持两种类型:
#### 2018-01-02V1.2.0
- 添加支持自定义主题颜色的方法。
#### 2017-11-26V1.1.0
- 更换第三方依赖库。
- 用MJExtension 替换了 原来的YYModel以前没有注意导入YYModel同时又导入YYKit会导致重复导入而冲突另外使用YYModel时手动导入和pod导入 其中的头文件和方法名也不一样,所以很容易出错)。
#### 2017-11-16V1.0.0
- 初始版本!
# 许可证
BRPickerView 使用 MIT 许可证,详情见 LICENSE 文件。

15
Pods/Manifest.lock generated
View File

@ -6,6 +6,16 @@ PODS:
- AMapFoundation-NO-IDFA (>= 1.9.0)
- AMapNavi-NO-IDFA (11.2.000):
- AMapFoundation-NO-IDFA (>= 1.9.0)
- BRPickerView (3.0.0):
- BRPickerView/Default (= 3.0.0)
- BRPickerView/Core (3.0.0)
- BRPickerView/DatePicker (3.0.0):
- BRPickerView/Core
- BRPickerView/Default (3.0.0):
- BRPickerView/DatePicker
- BRPickerView/TextPicker
- BRPickerView/TextPicker (3.0.0):
- BRPickerView/Core
- CocoaLumberjack/Core (3.9.1)
- CocoaLumberjack/Swift (3.9.1):
- CocoaLumberjack/Core
@ -178,6 +188,7 @@ DEPENDENCIES:
- AlipaySDK-iOS
- AMapLocation-NO-IDFA
- AMapNavi-NO-IDFA
- BRPickerView
- CocoaLumberjack/Swift
- CocoaMQTT
- GYSDK
@ -220,6 +231,7 @@ SPEC REPOS:
- AMapFoundation-NO-IDFA
- AMapLocation-NO-IDFA
- AMapNavi-NO-IDFA
- BRPickerView
- CocoaLumberjack
- CocoaMQTT
- Differentiator
@ -284,6 +296,7 @@ SPEC CHECKSUMS:
AMapFoundation-NO-IDFA: a2e3c895398d7ee757278e1a0a8f9359da4b146e
AMapLocation-NO-IDFA: 7cd8fc837ea41edfbf4d937cd20572e277b77d18
AMapNavi-NO-IDFA: d55b9b138c6e540f737c689975cae2c9dbd1026a
BRPickerView: cd2e0e3aa87af062bc7a1b6f78d3dfd5f04f04bb
CocoaLumberjack: e4ba3b414dfca8c1916c6303d37f63b3a95134c6
CocoaMQTT: 1e2fa493d57045d66fb1eba2bb5ffb8d77039c60
Differentiator: e8497ceab83c1b10ca233716d547b9af21b9344d
@ -334,6 +347,6 @@ SPEC CHECKSUMS:
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
PODFILE CHECKSUM: 4d254b7f0c48f2a2100703f9ac600d5d826b586a
PODFILE CHECKSUM: 848cd9ba02b2f1c209ac9eef708dd2c43b005385
COCOAPODS: 1.16.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "941104F296513E17138328984384286C"
BuildableName = "BRPickerView.Privacy.bundle"
BlueprintName = "BRPickerView-BRPickerView.Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D505645C3F99EB1E8B6529D64D2C3E1C"
BuildableName = "BRPickerView.framework"
BlueprintName = "BRPickerView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -34,6 +34,16 @@
<key>isShown</key>
<false/>
</dict>
<key>BRPickerView-BRPickerView.Privacy.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>BRPickerView.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>CocoaLumberjack-CocoaLumberjackPrivacy.xcscheme</key>
<dict>
<key>isShown</key>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_BRPickerView : NSObject
@end
@implementation PodsDummy_BRPickerView
@end

View File

@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -0,0 +1,26 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "BRPickerView.h"
#import "BRPickerAlertView.h"
#import "BRPickerStyle.h"
#import "BRPickerViewMacro.h"
#import "NSBundle+BRPickerView.h"
#import "BRDatePickerView+BR.h"
#import "BRDatePickerView.h"
#import "NSDate+BRPickerView.h"
#import "BRTextModel.h"
#import "BRTextPickerView.h"
FOUNDATION_EXPORT double BRPickerViewVersionNumber;
FOUNDATION_EXPORT const unsigned char BRPickerViewVersionString[];

View File

@ -0,0 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/BRPickerView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,6 @@
framework module BRPickerView {
umbrella header "BRPickerView-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/BRPickerView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -64,6 +64,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## BRPickerView
MIT License
Copyright (c) 2019 91renb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## CocoaLumberjack
BSD 3-Clause License

View File

@ -105,6 +105,37 @@ SOFTWARE.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2019 91renb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>BRPickerView</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>BSD 3-Clause License

View File

@ -1,5 +1,6 @@
${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework
${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework
${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework
${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework

View File

@ -1,4 +1,5 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BRPickerView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaMQTT.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Differentiator.framework

View File

@ -1,5 +1,6 @@
${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework
${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework
${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework
${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework

View File

@ -1,4 +1,5 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BRPickerView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaMQTT.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Differentiator.framework

View File

@ -177,6 +177,7 @@ code_sign_if_enabled() {
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework"
install_framework "${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework"
@ -223,6 +224,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework"
install_framework "${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework"

View File

@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
ARCHS = $(ARCHS_STANDARD)
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
ENABLE_USER_SCRIPT_SANDBOXING = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture/RxGesture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView/BRPickerView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture/RxGesture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"iconv" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CallKit" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreServices" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxGesture" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapNavi-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"iconv" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "BRPickerView" -framework "CFNetwork" -framework "CallKit" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreServices" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxGesture" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapNavi-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
ARCHS = $(ARCHS_STANDARD)
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
ENABLE_USER_SCRIPT_SANDBOXING = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture/RxGesture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView/BRPickerView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxGesture/RxGesture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"iconv" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CallKit" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreServices" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxGesture" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapNavi-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"iconv" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "BRPickerView" -framework "CFNetwork" -framework "CallKit" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreServices" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxGesture" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapNavi-NO-IDFA" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxGesture" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@ -195,6 +195,17 @@
30CCDE582FE39F8C00F5214A /* SOSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE572FE39F8C00F5214A /* SOSView.swift */; };
30CCDE5A2FE39F9D00F5214A /* SOSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE592FE39F9D00F5214A /* SOSViewController.swift */; };
30CCDE5C2FE3A1A800F5214A /* SOSPracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE5B2FE3A1A800F5214A /* SOSPracticeView.swift */; };
30D74AAB2FE8C7700050EB2C /* GPSSignalHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AAA2FE8C7700050EB2C /* GPSSignalHelper.swift */; };
30D74AAE2FEA13E00050EB2C /* ScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AAD2FEA13E00050EB2C /* ScheduleView.swift */; };
30D74AB02FEA13ED0050EB2C /* ScheduleVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AAF2FEA13ED0050EB2C /* ScheduleVC.swift */; };
30D74AB22FEA1D5D0050EB2C /* ScheduleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AB12FEA1D5D0050EB2C /* ScheduleViewModel.swift */; };
30D74AB42FEA25B90050EB2C /* ViewedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AB32FEA25B90050EB2C /* ViewedModel.swift */; };
30D74AB62FEA34FF0050EB2C /* ItineraryAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AB52FEA34FF0050EB2C /* ItineraryAPI.swift */; };
30D74AB82FEA36A50050EB2C /* ItineraryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AB72FEA36A50050EB2C /* ItineraryService.swift */; };
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AB92FEA37AD0050EB2C /* ScheduleModel.swift */; };
30D74ABD2FEA67EA0050EB2C /* CreateScheduleVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74ABC2FEA67EA0050EB2C /* CreateScheduleVC.swift */; };
30D74ABF2FEA67F30050EB2C /* CreateScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74ABE2FEA67F30050EB2C /* CreateScheduleView.swift */; };
30D74AC12FEA6EEF0050EB2C /* CreateSchedulePopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D74AC02FEA6EEF0050EB2C /* CreateSchedulePopView.swift */; };
30D87CDB2FDFA9EE00E958FD /* MQTTService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDA2FDFA9EE00E958FD /* MQTTService.swift */; };
30D87CDD2FDFF07500E958FD /* InteractionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDC2FDFF07500E958FD /* InteractionView.swift */; };
30D87CDF2FDFF1A100E958FD /* QuickMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */; };
@ -441,6 +452,17 @@
30CCDE572FE39F8C00F5214A /* SOSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOSView.swift; sourceTree = "<group>"; };
30CCDE592FE39F9D00F5214A /* SOSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOSViewController.swift; sourceTree = "<group>"; };
30CCDE5B2FE3A1A800F5214A /* SOSPracticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOSPracticeView.swift; sourceTree = "<group>"; };
30D74AAA2FE8C7700050EB2C /* GPSSignalHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPSSignalHelper.swift; sourceTree = "<group>"; };
30D74AAD2FEA13E00050EB2C /* ScheduleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleView.swift; sourceTree = "<group>"; };
30D74AAF2FEA13ED0050EB2C /* ScheduleVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleVC.swift; sourceTree = "<group>"; };
30D74AB12FEA1D5D0050EB2C /* ScheduleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleViewModel.swift; sourceTree = "<group>"; };
30D74AB32FEA25B90050EB2C /* ViewedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewedModel.swift; sourceTree = "<group>"; };
30D74AB52FEA34FF0050EB2C /* ItineraryAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItineraryAPI.swift; sourceTree = "<group>"; };
30D74AB72FEA36A50050EB2C /* ItineraryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItineraryService.swift; sourceTree = "<group>"; };
30D74AB92FEA37AD0050EB2C /* ScheduleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleModel.swift; sourceTree = "<group>"; };
30D74ABC2FEA67EA0050EB2C /* CreateScheduleVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateScheduleVC.swift; sourceTree = "<group>"; };
30D74ABE2FEA67F30050EB2C /* CreateScheduleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateScheduleView.swift; sourceTree = "<group>"; };
30D74AC02FEA6EEF0050EB2C /* CreateSchedulePopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSchedulePopView.swift; sourceTree = "<group>"; };
30D87CDA2FDFA9EE00E958FD /* MQTTService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTService.swift; sourceTree = "<group>"; };
30D87CDC2FDFF07500E958FD /* InteractionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionView.swift; sourceTree = "<group>"; };
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickMessageView.swift; sourceTree = "<group>"; };
@ -497,21 +519,29 @@
/* Begin PBXFileSystemSynchronizedRootGroup section */
30CCDF8E2FE3E63B00F5214A /* sound */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = sound;
sourceTree = "<group>";
};
30CCDF902FE3E63B00F5214A /* video */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = video;
sourceTree = "<group>";
};
30CCE01E2FE3E64700F5214A /* lotties */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = lotties;
sourceTree = "<group>";
};
30D87CEF2FDFF52100E958FD /* TTGTagCollectionView */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = TTGTagCollectionView;
sourceTree = "<group>";
};
@ -551,6 +581,7 @@
305A74CF2FCA8C7000227D26 /* UserAPI.swift */,
30BAB8502FCD331C00C33B5C /* GroupAPI.swift */,
30D891F42FE22E0600E958FD /* OrderAPI.swift */,
30D74AB52FEA34FF0050EB2C /* ItineraryAPI.swift */,
);
path = API;
sourceTree = "<group>";
@ -950,6 +981,7 @@
305A762A2FCA8C7000227D26 /* Home */,
305A76352FCA8C7000227D26 /* Map */,
305A76262FCA8C7000227D26 /* Group */,
30D74AAC2FEA13BD0050EB2C /* Schedule */,
305A76392FCA8C7000227D26 /* Mine */,
305A798E2FCAC5F600227D26 /* InviteMember */,
3062E8C52FCFD01000CEF511 /* VipRecharge */,
@ -969,6 +1001,7 @@
305A763C2FCA8C7000227D26 /* UserService.swift */,
30BAB8522FCD337C00C33B5C /* GroupService.swift */,
30D891F62FE22E6E00E958FD /* OrderService.swift */,
30D74AB72FEA36A50050EB2C /* ItineraryService.swift */,
);
path = Service;
sourceTree = "<group>";
@ -995,6 +1028,7 @@
305A76452FCA8C7000227D26 /* Tool */ = {
isa = PBXGroup;
children = (
30D74AAA2FE8C7700050EB2C /* GPSSignalHelper.swift */,
305A76412FCA8C7000227D26 /* AutoLayout */,
305A76442FCA8C7000227D26 /* Router */,
);
@ -1241,6 +1275,29 @@
path = SOS;
sourceTree = "<group>";
};
30D74AAC2FEA13BD0050EB2C /* Schedule */ = {
isa = PBXGroup;
children = (
30D74AAF2FEA13ED0050EB2C /* ScheduleVC.swift */,
30D74AB12FEA1D5D0050EB2C /* ScheduleViewModel.swift */,
30D74AAD2FEA13E00050EB2C /* ScheduleView.swift */,
30D74AB32FEA25B90050EB2C /* ViewedModel.swift */,
30D74AB92FEA37AD0050EB2C /* ScheduleModel.swift */,
30D74ABB2FEA67CE0050EB2C /* CreateSchedule */,
);
path = Schedule;
sourceTree = "<group>";
};
30D74ABB2FEA67CE0050EB2C /* CreateSchedule */ = {
isa = PBXGroup;
children = (
30D74ABC2FEA67EA0050EB2C /* CreateScheduleVC.swift */,
30D74ABE2FEA67F30050EB2C /* CreateScheduleView.swift */,
30D74AC02FEA6EEF0050EB2C /* CreateSchedulePopView.swift */,
);
path = CreateSchedule;
sourceTree = "<group>";
};
30D87CD52FDF9F1900E958FD /* MQTT */ = {
isa = PBXGroup;
children = (
@ -1498,14 +1555,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks.sh\"\n";
@ -1519,14 +1572,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-resources.sh\"\n";
@ -1542,6 +1591,7 @@
305A76882FCA8C7000227D26 /* MoyaProvider+Rx.swift in Sources */,
305A76892FCA8C7000227D26 /* Observable+Response.swift in Sources */,
305A768A2FCA8C7000227D26 /* Single+Response.swift in Sources */,
30D74AB62FEA34FF0050EB2C /* ItineraryAPI.swift in Sources */,
305A768B2FCA8C7000227D26 /* API.swift in Sources */,
30EFF3AE2FD7FF1400EB35D4 /* TextInputViewController.swift in Sources */,
305A768C2FCA8C7000227D26 /* APIProvider.swift in Sources */,
@ -1585,6 +1635,7 @@
305A76A32FCA8C7000227D26 /* ControlEvents+Block.swift in Sources */,
3062E8B72FCE6BFE00CEF511 /* ScanView.swift in Sources */,
305A76A42FCA8C7000227D26 /* Date+Extension.swift in Sources */,
30D74AAE2FEA13E00050EB2C /* ScheduleView.swift in Sources */,
305A76A52FCA8C7000227D26 /* Dictionay+Extension.swift in Sources */,
305A76A62FCA8C7000227D26 /* Int+Extension.swift in Sources */,
30EFF3D62FDA8F0100EB35D4 /* EmergencyContactView.swift in Sources */,
@ -1597,6 +1648,7 @@
305A76AB2FCA8C7000227D26 /* ScaleType.swift in Sources */,
30EFF3E72FDAA93D00EB35D4 /* PrivacyPolicyView.swift in Sources */,
305A76AC2FCA8C7000227D26 /* String+Extension.swift in Sources */,
30D74AB82FEA36A50050EB2C /* ItineraryService.swift in Sources */,
30EFF3C62FDA433E00EB35D4 /* ChangePhoneView.swift in Sources */,
305A76AD2FCA8C7000227D26 /* UIApplicationExtension.swift in Sources */,
305A76AE2FCA8C7000227D26 /* UIButton+Extension.swift in Sources */,
@ -1636,6 +1688,7 @@
30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */,
305A76C52FCA8C7000227D26 /* Account.swift in Sources */,
305A76C62FCA8C7000227D26 /* AppContextManager.swift in Sources */,
30D74ABD2FEA67EA0050EB2C /* CreateScheduleVC.swift in Sources */,
305A76C72FCA8C7000227D26 /* UserConfigModel.swift in Sources */,
305A76C82FCA8C7000227D26 /* UserConfigResponse.swift in Sources */,
305A76C92FCA8C7000227D26 /* ApiManager.swift in Sources */,
@ -1644,6 +1697,7 @@
30CCDE552FE2903100F5214A /* SignInModel.swift in Sources */,
305A76CC2FCA8C7000227D26 /* FileTools.swift in Sources */,
305A76CD2FCA8C7000227D26 /* Permission.swift in Sources */,
30D74AB02FEA13ED0050EB2C /* ScheduleVC.swift in Sources */,
305A76CE2FCA8C7000227D26 /* RouterManager.swift in Sources */,
3062E8C72FCFD02F00CEF511 /* VipRechargeView.swift in Sources */,
305A76CF2FCA8C7000227D26 /* CountDownService.swift in Sources */,
@ -1668,10 +1722,12 @@
305A76DE2FCA8C7000227D26 /* ObservableType+ObjectMapper.swift in Sources */,
305A76DF2FCA8C7000227D26 /* Single+ObjectMapper.swift in Sources */,
30DC18602FD12A020041DCD1 /* VipWaivePopView.swift in Sources */,
30D74AAB2FE8C7700050EB2C /* GPSSignalHelper.swift in Sources */,
305A76E02FCA8C7000227D26 /* GroupView.swift in Sources */,
30EFF3BB2FD90D7600EB35D4 /* ConfirmPopVC.swift in Sources */,
30BAB8512FCD331C00C33B5C /* GroupAPI.swift in Sources */,
305A76E12FCA8C7000227D26 /* GroupViewController.swift in Sources */,
30D74AB22FEA1D5D0050EB2C /* ScheduleViewModel.swift in Sources */,
30EFF3B52FD8F1D000EB35D4 /* ReviewMemberListVC.swift in Sources */,
30BAB84D2FCD2FDE00C33B5C /* InviteJoinView.swift in Sources */,
30EFF3CF2FDA669800EB35D4 /* MyProfileVC.swift in Sources */,
@ -1697,11 +1753,13 @@
305A76EA2FCA8C7000227D26 /* OneTapLoginView.swift in Sources */,
305A76EB2FCA8C7000227D26 /* CircleMember.swift in Sources */,
30CCDE5A2FE39F9D00F5214A /* SOSViewController.swift in Sources */,
30D74AB42FEA25B90050EB2C /* ViewedModel.swift in Sources */,
305A76EC2FCA8C7000227D26 /* MemberAnnotation.swift in Sources */,
305A76ED2FCA8C7000227D26 /* MemberAnnotationView.swift in Sources */,
30BAB8532FCD337C00C33B5C /* GroupService.swift in Sources */,
305A76EE2FCA8C7000227D26 /* MineView.swift in Sources */,
305A76EF2FCA8C7000227D26 /* MineViewController.swift in Sources */,
30D74ABF2FEA67F30050EB2C /* CreateScheduleView.swift in Sources */,
305A76F02FCA8C7000227D26 /* MineViewModel.swift in Sources */,
305A76F12FCA8C7000227D26 /* SystemService.swift in Sources */,
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */,
@ -1740,6 +1798,7 @@
305A77072FCA8C7000227D26 /* Helper.swift in Sources */,
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */,
30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */,
305A77092FCA8C7000227D26 /* PageContentView.swift in Sources */,
305A770A2FCA8C7000227D26 /* PageStyle.swift in Sources */,
@ -1759,6 +1818,7 @@
3062E8BA2FCEAC6500CEF511 /* CreateGroupView.swift in Sources */,
30D891F72FE22E6E00E958FD /* OrderService.swift in Sources */,
305A77192FCA8C7000227D26 /* CollectionHFlowLayout.swift in Sources */,
30D74AC12FEA6EEF0050EB2C /* CreateSchedulePopView.swift in Sources */,
305A771A2FCA8C7000227D26 /* JJPageControl.swift in Sources */,
305A771B2FCA8C7000227D26 /* ReusableView.swift in Sources */,
305A771C2FCA8C7000227D26 /* AppDelegate.swift in Sources */,

View File

@ -0,0 +1,55 @@
//
// ItineraryAPI.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import Moya
import SwiftyUserDefaults
internal import Alamofire
/// API
enum ItineraryAPI {
///
/// - Parameters:
/// - follow:
/// - own:
/// - history: true
/// - group_key:
case query(follow: Bool, own: Bool, history: Bool, group_key: String, page: Int)
}
extension ItineraryAPI: MultiTargetProtocol {
var path: String {
switch self {
case .query:
return "mapi/itinerary/route"
}
}
var method: Moya.Method {
switch self {
case .query:
return .get
default:
return .post
}
}
var task: Moya.Task {
switch self {
case let .query(follow, own, history, group_key, page):
var params = Parameters()
params["follow"] = follow
params["own"] = own
params["history"] = history
params["group_key"] = group_key
params["page"] = page
params["limit"] = 20
return .requestParameters(parameters: params, encoding: URLEncoding())
}
}
}

View File

@ -20,6 +20,9 @@ enum SystemAPI {
///
case wechatService
/// SOS
case sos(enable: Bool)
}
extension SystemAPI: MultiTargetProtocol {
@ -32,6 +35,8 @@ extension SystemAPI: MultiTargetProtocol {
return "api/user/sms/code"
case .wechatService:
return "api/weixin/service"
case .sos:
return "mapi/sos/operate"
}
}
@ -39,7 +44,7 @@ extension SystemAPI: MultiTargetProtocol {
switch self {
case .userConfig, .wechatService:
return .get
case .sendCode:
case .sendCode, .sos:
return .post
}
}
@ -56,6 +61,12 @@ extension SystemAPI: MultiTargetProtocol {
case .wechatService:
return .requestParameters(parameters: parameters, encoding: URLEncoding())
case let .sos(enable):
var params = Parameters()
params["enable"] = enable
return .requestParameters(parameters: params, encoding: JSONEncoding())
}
}
}

View File

@ -60,6 +60,12 @@ enum UserAPI {
/// - group_keykey
/// - emote_idx
case sendEmote(emoteIdx: Int, groupKey: String, targetUid: String)
///
case notice
///
case followList
}
extension UserAPI: MultiTargetProtocol {
@ -90,12 +96,16 @@ extension UserAPI: MultiTargetProtocol {
return "api/user/logout"
case .sendEmote:
return "mapi/user/sendemote"
case .notice:
return "api/user/notice"
case .followList:
return "mapi/user/followed"
}
}
var method: Moya.Method {
switch self {
case .userInfo, .signInInfo:
case .userInfo, .signInInfo, .notice, .followList:
return .get
case .changePhone, .setGender:
return .put
@ -167,6 +177,12 @@ extension UserAPI: MultiTargetProtocol {
params["group_key"] = groupKey
params["target_uid"] = targetUid
return .requestParameters(parameters: params, encoding: JSONEncoding())
case .notice:
return .requestParameters(parameters: Parameters(), encoding: URLEncoding())
case .followList:
return .requestParameters(parameters: Parameters(), encoding: URLEncoding())
}
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2309@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2309@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "x@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "x@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2267@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2267@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2250@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2250@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2308@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2308@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2264@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2264@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -28,6 +28,8 @@ extension Notification.Name {
static let RefreshUserConfigNotification = Notification.Name("RefreshUserConfigNotification")
///
static let RefreshGroupInfoNotification = Notification.Name("RefreshGroupInfoNotification")
/// IM
static let RefreshIMGroupListNotification = Notification.Name("RefreshIMGroupListNotification")
/// /
static let RequestOrderPayStatusNotification = Notification.Name("RequestOrderPayStatusNotification")
}

View File

@ -9,7 +9,7 @@ final class MainTabBarController: UITabBarController {
// MARK: - View Controllers
private lazy var locationNav = BaseNavigationController(rootViewController: HomeViewController())
private lazy var exploreNav = BaseNavigationController(rootViewController: ViewController())
private lazy var exploreNav = BaseNavigationController(rootViewController: ScheduleVC())
private lazy var circlesNav = BaseNavigationController(rootViewController: GroupViewController())
private lazy var mineNav = BaseNavigationController(rootViewController: MineViewController())
@ -57,6 +57,14 @@ final class MainTabBarController: UITabBarController {
view.addSubview(customTabBar)
customTabBar.delegate = self
// ""index=1
customTabBar.shouldSelectTab = { index in
if index == 1, AppContextManager.shared.isGuest {
AppRouter.push(Route.login)
return false
}
return true
}
customTabBar.translatesAutoresizingMaskIntoConstraints = false
let tabBarHeight = customTabBar.intrinsicContentSize.height
@ -89,6 +97,11 @@ final class MainTabBarController: UITabBarController {
// MARK: - QuickLocationTabBarDelegate
extension MainTabBarController: QuickLocationTabBarDelegate {
func tabBar(_ tabBar: QuickLocationTabBar, didSelectTabAt index: Int) {
// ""index=1
if index == 1, AppContextManager.shared.isGuest {
AppRouter.push(Route.login)
return
}
selectedIndex = index
}
}

View File

@ -40,6 +40,8 @@ final class QuickLocationTabBar: UIView {
// MARK: - Properties
weak var delegate: QuickLocationTabBarDelegate?
/// false
var shouldSelectTab: ((Int) -> Bool)?
private let items: [TabItem]
private var tabViews: [UIView] = []
@ -121,6 +123,7 @@ final class QuickLocationTabBar: UIView {
guard let view = gesture.view else { return }
let index = view.tag
guard index != selectedIndex else { return }
if shouldSelectTab?(index) == false { return }
selectedIndex = index
updateSelection(animated: true)
delegate?.tabBar(self, didSelectTabAt: index)

View File

@ -53,6 +53,8 @@ enum Route: String {
case privacyPolicy = "privacyPolicy"
///
case checkPermission = "checkPermission"
///
case createSchedule = "createSchedule"
}
extension Route: RouterTarget {
@ -257,6 +259,11 @@ extension AppRouter: AppRouterProtocol {
AppRouter.register(Route.checkPermission) { url, parameters in
CheckPermissionVC()
}
// MARK: -
AppRouter.register(Route.createSchedule) { url, parameters in
CreateScheduleVC()
}
}
}

View File

@ -82,7 +82,7 @@ class CreateGroupViewModel {
"description": desc,
"labels": selectedTagList]).subscribe(onNext: { response in
DLToast.showSuccess(text: "创建成功") {
NotificationCenter.default.post(name: .RefreshGroupInfoNotification, object: nil)
NotificationCenter.default.post(name: .RefreshIMGroupListNotification, object: nil)
AppRouter.shared.popOrDismiss()
}
}, onError: { (error) in

View File

@ -406,7 +406,7 @@ final class GroupChatVC: BaseViewController {
}
guard let url = recordFileURL, recordDuration >= 1 else {
DLToast.show(text: "说话时间太短")
self.dl.show(text: "说话时间太短")
if let url = recordFileURL { try? FileManager.default.removeItem(at: url) }
return
}

View File

@ -39,10 +39,10 @@ class GroupInfoVC: BaseViewController {
// MARK: - API
private func requestOperateGroup() {
guard let model = groupInModel else { return }
DLToast.showLoading()
dl.showLoading()
GroupService.operate(opType: "join",
requestData: ["share_code" : model.share_code]).subscribe(onNext: { response in
DLToast.show(text: "申请成功")
self.dl.show(text: "申请成功")
}, onError: { (error) in }).disposed(by: disposeBag)
}

View File

@ -168,20 +168,20 @@ class GroupSettingVC: BaseViewController {
}
private func requestLeaveGroup() {
DLToast.showLoading()
dl.showLoading()
GroupService.leave(requestData: ["group_key": viewModel.groupId]).subscribe { response in
DLToast.dismiss()
DLToast.show(text: "成功退出") {
self.dl.dismiss()
self.dl.show(text: "成功退出") {
AppRouter.shared.popToRoot()
}
}.disposed(by: disposeBag)
}
private func requestDismissGroup() {
DLToast.showLoading()
dl.showLoading()
GroupService.dismiss(requestData: ["group_key": viewModel.groupId]).subscribe { response in
DLToast.dismiss()
DLToast.show(text: "成功解散") {
self.dl.dismiss()
self.dl.show(text: "成功解散") {
AppRouter.shared.popToRoot()
}
}.disposed(by: disposeBag)

View File

@ -141,6 +141,13 @@ final class GroupViewController: BaseViewController {
})
.disposed(by: disposeBag)
// MQTT join/leave/dismiss
NotificationCenter.default.rx.notification(.RefreshIMGroupListNotification)
.subscribe(onNext: { [weak self] _ in
self?.requestGroupInfo()
})
.disposed(by: disposeBag)
NotificationCenter.default.rx.notification(.RefreshUserConfigNotification)
.subscribe(onNext: { [weak self] _ in
self?.requestRecommandGroup()

View File

@ -30,6 +30,14 @@ class JoinGroupVC: BaseViewController {
rootView.submitBtn.rx.tap.subscribe(onNext: { _ in
self.requestOperateGroup()
}).disposed(by: disposeBag)
rootView.scanBtn.rx.tap.subscribe(onNext: { _ in
let vc = ScanVC { code in
self.rootView.textField.text = code
self.rootView.textField.resignFirstResponder()
}
AppRouter.push(vc)
}).disposed(by: disposeBag)
}
override func viewDidAppear(_ animated: Bool) {
@ -43,9 +51,9 @@ class JoinGroupVC: BaseViewController {
DLToast.show(text: "请填写完整的邀请码")
return
}
DLToast.showLoading()
dl.showLoading()
GroupService.operate(opType: "join", requestData: ["share_code" : rootView.inviteCode]).subscribe(onNext: { response in
DLToast.showSuccess(text: "申请成功") {
self.dl.showSuccess(text: "申请成功") {
AppRouter.shared.popOrDismiss()
}
}).disposed(by: disposeBag)

View File

@ -55,9 +55,7 @@ class JoinGroupView: UIView {
AppRouter.shared.popOrDismiss()
}).disposed(by: disposeBag)
scanBtn.rx.tap.subscribe(onNext: { _ in
AppRouter.push(Route.scan)
}).disposed(by: disposeBag)
}
private func setupUI() {

View File

@ -11,6 +11,7 @@ import RxCocoa
import Lottie
#if !targetEnvironment(simulator)
import AMapNaviKit
import MarqueeLabel
#endif
class HomeView: UIView {
@ -23,6 +24,16 @@ class HomeView: UIView {
let interactionView = InteractionView(frame: CGRectMake(0, kScreenHeight, kScreenWidth, 384))
var gpsSignalViewList: [UIView] = []
///
func updateGPSSignal(bars: Int) {
let filled = max(0, min(bars, gpsSignalViewList.count))
for (i, view) in gpsSignalViewList.enumerated() {
view.backgroundColor = i < filled ? UIColor(hexStr: "#4ED178") : UIColor(hexStr: "#E0E0E0")
}
}
// MARK: - groupMemberView
private var groupMemberTopConstraint: NSLayoutConstraint?
private var groupMemberUpLimit: CGFloat = 0
@ -91,11 +102,7 @@ class HomeView: UIView {
}
})
.disposed(by: disposeBag)
// view
quickMessageView.closeBtn.rx.tap.subscribe(onNext: { _ in
self.dismissMemberPanel()
}).disposed(by: disposeBag)
}
private func setupUI() {
@ -175,7 +182,7 @@ class HomeView: UIView {
.edgesHorzontal(15)
toolsView.layoutChain
.left(23)
.left(15)
.bottom(kScreenHeight / 2 - 58)
.width(40)
@ -279,6 +286,13 @@ class HomeView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
noticeView.layoutIfNeeded()
noticeInfoView.setGradientLayer(frame: noticeInfoView.bounds,
startPoint: CGPoint(x: 0, y: 0.5),
endPoint: CGPoint(x: 1, y: 0.5),
colors: [UIColor(hexStr: "#FFFFFF", alpha: 0), UIColor(hexStr: "#FFFFFF"), UIColor(hexStr: "#FFFFFF", alpha: 0)],
locations: [0, 0.5, 1])
if !isGroupMemberLimitsSet {
isGroupMemberLimitsSet = true
groupMemberDownLimit = groupMemberView.frame.minY
@ -438,25 +452,153 @@ class HomeView: UIView {
}()
lazy var messageStackView: UIStackView = {
let view = UIStackView(arrangedSubviews: [noticeView, messageBubbleView])
let view = UIStackView(arrangedSubviews: [tipsView, messageBubbleView])
view.axis = .vertical
view.alignment = .leading
view.spacing = 15
view.spacing = 5
view.backgroundColor = .clear
return view
}()
lazy var tipsView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.layoutChain.height(22)
view.addSubview(gpsSignalView)
gpsSignalView.layoutChain
.left()
.centerY()
view.addSubview(noticeView)
noticeView.layoutChain
.leftToRightOfView(gpsSignalView, offset: 0)
.right(15)
.edgesVertical()
return view
}()
// MARK: -
lazy var gpsSignalView: UIView = {
let view = UIView()
view.cornerRadius = 5
view.backgroundColor = .black.withAlphaComponent(0.4)
let signalView1 = UIView()
signalView1.backgroundColor = UIColor(hexStr: "#E6E6E6")
view.addSubview(signalView1)
signalView1.layoutChain
.left(7)
.bottom(5)
.width(3)
.height(4)
let signalView2 = UIView()
signalView2.backgroundColor = UIColor(hexStr: "#E6E6E6")
view.addSubview(signalView2)
signalView2.layoutChain
.leftToRightOfView(signalView1, offset: 2)
.bottomToView(signalView1)
.width(3)
.height(6)
let signalView3 = UIView()
signalView3.backgroundColor = UIColor(hexStr: "#E6E6E6")
view.addSubview(signalView3)
signalView3.layoutChain
.leftToRightOfView(signalView2, offset: 2)
.bottomToView(signalView1)
.width(3)
.height(8)
let signalView4 = UIView()
signalView4.backgroundColor = UIColor(hexStr: "#E6E6E6")
view.addSubview(signalView4)
signalView4.layoutChain
.leftToRightOfView(signalView3, offset: 2)
.bottomToView(signalView1)
.width(3)
.height(10)
let label = UILabel()
label.text = "卫星信号"
label.font = .systemFont(ofSize: 10, weight: .medium)
label.textColor = .white
view.addSubview(label)
label.layoutChain
.leftToRightOfView(signalView4, offset: 2)
.edgesVertical(4)
.right(7)
gpsSignalViewList = [signalView1, signalView2, signalView3, signalView4]
return view
}()
// MARK: -
lazy var noticeView: UIView = {
let view = UIView()
view.isHidden = true
view.backgroundColor = .clear
view.layoutChain
.height(22)
view.addSubview(noticeInfoView)
noticeInfoView.layoutChain
.left()
.right()
.edgesVertical()
view.addSubview(noticeCloseBtn)
noticeCloseBtn.layoutChain
.right()
.width(16)
.height(16)
.centerY()
return view
}()
lazy var noticeInfoView: UIView = {
let view = UIView()
view.backgroundColor = .clear
// let titleLab = UILabel()
// titleLab.text = ""
// titleLab.font = .systemFont(ofSize: 10, weight: .medium)
// titleLab.textColor = UIColor(hexStr: "#0F2948")
// view.addSubview(titleLab)
// titleLab.layoutChain
// .left(16)
// .centerY()
// .width(40)
view.addSubview(noticeLab)
noticeLab.layoutChain
.left(16)
.centerY()
.right(24)
return view
}()
lazy var noticeLab: CallbackMarqueeLabel = {
let label = CallbackMarqueeLabel()
label.font = .systemFont(ofSize: 10, weight: .medium)
label.textColor = UIColor(hexStr: "#0F2948")
return label
}()
lazy var noticeCloseBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "Common/x_black"), for: .normal)
btn.backgroundColor = .white
btn.cornerRadius = 8
btn.extendEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
btn.rx.tap.subscribe(onNext: { [weak self] _ in
self?.noticeView.isHidden = true
}).disposed(by: disposeBag)
return btn
}()
// MARK: -
lazy var messageBubbleView: UIView = {
let view = UIView()
@ -654,3 +796,24 @@ extension HomeView: UIGestureRecognizerDelegate {
return true
}
}
// MARK: - CallbackMarqueeLabel
class CallbackMarqueeLabel: MarqueeLabel {
//
var onScrollLoopComplete: (() -> Void)?
var onScrollStart: (() -> Void)?
//
override func labelWillBeginScroll() {
super.labelWillBeginScroll()
onScrollStart?()
}
//
override func labelReturnedToHome(_ finished: Bool) {
super.labelReturnedToHome(finished)
//
guard finished else { return }
onScrollLoopComplete?()
}
}

View File

@ -17,7 +17,7 @@ import SwiftyUserDefaults
import AMapNaviKit
import CocoaMQTT
import Lottie
import AVFAudio
import MarqueeLabel
#endif
class HomeViewController: BaseViewController {
@ -52,6 +52,8 @@ class HomeViewController: BaseViewController {
private var offlineCheckTimer: Timer?
private var sosPlayerKey: UInt8 = 0
private var groupRefreshWorkItem: DispatchWorkItem?
private var groupSwitchWorkItem: DispatchWorkItem?
override func loadView() {
#if !targetEnvironment(simulator)
@ -71,7 +73,7 @@ class HomeViewController: BaseViewController {
reactiveAction()
requestUserConfig()
rootView.quickMessageView.tagListView.delegate = self
// MQTT
UIDevice.current.isBatteryMonitoringEnabled = true
@ -250,6 +252,8 @@ class HomeViewController: BaseViewController {
self.requestUserInfo { [weak self] in
self?.requestGroupInfo()
}
//
self.requestNotice()
}).disposed(by: disposeBag)
}
@ -274,10 +278,13 @@ class HomeViewController: BaseViewController {
}.disposed(by: disposeBag)
}
private func requestGroupInfo() {
private func requestGroupInfo(isDefaultGroup: Bool=true) {
GroupService.groupInfo().subscribe { response in
guard let model = response.model else { return }
self.viewModel.groupModel = model
NotificationCenter.default.post(name: .RefreshIMGroupListNotification, object: nil)
guard isDefaultGroup else { return }
self.rootView.groupMemberView.setupCountData(self.viewModel.memberCount, 1)
self.rootView.groupNameLab.text = self.viewModel.groupName
self.syncMemberAnnotations(model.select_group_employee)
@ -291,6 +298,22 @@ class HomeViewController: BaseViewController {
}.disposed(by: disposeBag)
}
private func requestNotice() {
UserService.notice().subscribe { response in
self.rootView.noticeLab.text = ""
guard let data = response.data, let noticeList = data["notice"] as? [String] else { return }
self.rootView.noticeView.isHidden = noticeList.count == 0
self.rootView.noticeLab.text = noticeList.joined(separator: " ") + " "
if let loop = data["loop"] as? Bool {
self.rootView.noticeLab.type = loop ? .continuous : .leftRight
self.rootView.noticeLab.onScrollLoopComplete = {
guard loop == false else { return }
self.rootView.noticeView.isHidden = true
}
}
}.disposed(by: disposeBag)
}
// MARK: - Map Setup
private func setupMap() {
#if !targetEnvironment(simulator)
@ -423,6 +446,24 @@ class HomeViewController: BaseViewController {
// MARK: - MQTT
extension HomeViewController {
/// join/leave/dismiss 500ms
private func debounceGroupSwitch(groupKey: String) {
groupSwitchWorkItem?.cancel()
let work = DispatchWorkItem { [weak self] in
self?.requestOperateGroup(groupKey: groupKey)
}
groupSwitchWorkItem = work
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: work)
}
private func debounceGroupRefresh() {
groupRefreshWorkItem?.cancel()
let work = DispatchWorkItem { [weak self] in
self?.requestGroupInfo()
}
groupRefreshWorkItem = work
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: work)
}
/// MQTT
private func refreshMQTTSubscriptions(_ members: [GroupMemberModel]) {
// let currentId = AppContextManager.shared.userId
@ -544,7 +585,10 @@ extension HomeViewController {
}
}
}
case "sos":
case "sos": //
self.requestGroupInfo(isDefaultGroup: false)
guard let userId = msg.data?.user_id,
userId != AppContextManager.shared.userId else { return }
playSOSAlarm()
rootView.sosPopView.isHidden = false
rootView.sosPopView.play(toFrame: 30.0) { completed in
@ -552,6 +596,55 @@ extension HomeViewController {
self.rootView.sosPopView.stop()
self.rootView.sosPopView.isHidden = true
}
case "join": //
guard let gk = msg.data?.group_key,
let defaultGk = self.viewModel.groupModel?.default_group_key,
gk == defaultGk else {
self.requestGroupInfo(isDefaultGroup: false)
return
}
debounceGroupRefresh()
case "leave": //
guard let userId = msg.data?.user_id, let gk = msg.data?.group_key,
let defaultGk = self.viewModel.groupModel?.default_group_key,
defaultGk.hasPrefix(gk) else {
self.requestGroupInfo(isDefaultGroup: false)
return
}
//
if userId == AppContextManager.shared.userId { //
guard let model = self.viewModel.groupModel,
let groupInfoModel = model.groups.first else { return }
if model.groups.count > 1 {
self.debounceGroupSwitch(groupKey: groupInfoModel.group_key)
}
else {
debounceGroupRefresh()
}
}
else { //
guard self.viewModel.memberIsExist(userId: userId) else { return }
self.viewModel.removeMember(userId: userId)
self.removeAnnotation(userId: userId)
self.rootView.groupMemberView.setupCountData(self.viewModel.memberList.count, 1)
MQTTService.shared.unsubscribe(topic: "smartdrive/\(userId)")
}
case "dismiss": //
guard let gk = msg.data?.group_key,
let defaultGk = self.viewModel.groupModel?.default_group_key,
gk == defaultGk,
let model = self.viewModel.groupModel,
let groupInfoModel = model.groups.first else {
self.requestGroupInfo(isDefaultGroup: false)
return
}
if model.groups.count > 1 {
self.debounceGroupSwitch(groupKey: groupInfoModel.group_key)
}
else {
debounceGroupRefresh()
}
default:
print("📩 未处理 type=\(msg.type ?? "")")
}
@ -638,6 +731,12 @@ extension HomeViewController: MAMapViewDelegate {
func mapView(_ mapView: MAMapView!, didUpdate userLocation: MAUserLocation!, updatingLocation: Bool) {
guard updatingLocation, let location = userLocation.location else { return }
//
let strength = gpsSignalStrength(from: location)
rootView.updateGPSSignal(bars: strength.barCount)
//
lastLocation = location
let coordinate = location.coordinate
guard CLLocationCoordinate2DIsValid(coordinate) else { return }
@ -662,6 +761,10 @@ extension HomeViewController {
}
private func playSOSAlarm() {
//
sosPlayer?.stop()
sosPlayer = nil
guard let url = Bundle.main.url(forResource: "sos", withExtension: "mp3") ?? Bundle.main.url(forResource: "sos", withExtension: "mp3", subdirectory: "sound") else {
print("❌ SOS: sos.mp3 not found in bundle")
return
@ -677,11 +780,11 @@ extension HomeViewController {
return
}
print("✅ SOS: playing alarm")
player.numberOfLoops = -1
player.numberOfLoops = 0
player.volume = 1.0
player.play()
self.sosPlayer = player
DispatchQueue.main.asyncAfter(deadline: .now() + 30) { [weak self] in
DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in
self?.sosPlayer?.stop()
self?.sosPlayer = nil
}

View File

@ -114,6 +114,19 @@ class HomeViewModel {
updated[idx].last_active_time = lastUpdateTime
memberList = updated // didSet + sectionedItems
}
///
func memberIsExist(userId: String) -> Bool {
(memberList.first(where: { $0.user_id == userId }) != nil)
}
///
func removeMember(userId: String) {
guard let idx = memberList.firstIndex(where: { $0.user_id == userId }) else { return }
var updated = memberList
updated.remove(at: idx)
memberList = updated
}
// MARK: - Init
init() {

View File

@ -18,6 +18,8 @@ class SOSView: UIView {
var sosPracticeView: SOSPracticeView = SOSPracticeView()
var didSendSOS: ((Bool) -> Void)?
var countDownFinish: Bool = false
private func setupRx() {
@ -37,6 +39,9 @@ class SOSView: UIView {
self.countDownLottieView.play()
self.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
self.countDownFinish = true
if let didSendSOS = self.didSendSOS {
didSendSOS(true)
}
}
}
})
@ -57,7 +62,9 @@ class SOSView: UIView {
case .ended:
if self.sliderIcon.frame.minX >= maxX - 5 {
if countDownFinish {
if let didSendSOS = self.didSendSOS {
didSendSOS(false)
}
}
else {
self.countDownView.alpha = 0
@ -313,16 +320,6 @@ class SOSView: UIView {
let isHidden = Defaults[\.sosIsPracticeList].first(where: { $0 == AppContextManager.shared.userId }) != nil
sosPracticeView.isHidden = isHidden
guard AppContextManager.shared.status == 1 else { return }
countDownView.alpha = 1
countDownFinish = true
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
self.countDownLottieView.animation = LottieAnimation.filepath(path)
self.countDownLottieView.loopMode = .loop
self.countDownLottieView.play()
self.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
}
}
required init?(coder aDecoder: NSCoder) {

View File

@ -6,6 +6,8 @@
//
import UIKit
import RxSwift
import Lottie
class SOSViewController: BaseViewController {
@ -21,17 +23,37 @@ class SOSViewController: BaseViewController {
// Do any additional setup after loading the view.
fd_interactivePopDisabled = true
// requestUserInfo()
rootView.didSendSOS = { status in
self.requestSOS(status)
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
private func requestUserInfo(completion: (() -> Void)? = nil) {
UserService.userInfo().subscribe { response in
guard let model = response.model else { return }
AppContextManager.shared.saveAccount(model)
guard model.status == 1 else { return }
self.rootView.countDownView.alpha = 1
self.rootView.countDownFinish = true
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
self.rootView.countDownLottieView.animation = LottieAnimation.filepath(path)
self.rootView.countDownLottieView.loopMode = .loop
self.rootView.countDownLottieView.play()
self.rootView.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
}
}.disposed(by: disposeBag)
}
private func requestSOS(_ enable: Bool) {
dl.showLoading()
SystemService.sos(enable: enable).subscribe(onNext: { response in
self.dl.show(text: enable ? "发送成功" : "取消成功") {
AppRouter.shared.popOrDismiss()
}
}, onError: { (error) in }).disposed(by: disposeBag)
}
*/
}

View File

@ -31,17 +31,17 @@ class SignInVC: BaseViewController {
requestSignInInfo()
rootView.signInLottieView.rx.tapGesture.subscribe(onNext: { [weak self] _ in
self?.dl.showLoading()
guard let self = self, let loc = self.lastLocation, isSignIn == false else { return }
DLToast.showLoading()
self.geocoder.reverseGeocodeLocation(loc) { [weak self] placemarks, error in
DLToast.dismiss()
self?.dl.dismiss()
guard let self = self else { return }
let address = placemarks?.first?.name
?? placemarks?.first?.thoroughfare
?? placemarks?.first?.locality
?? ""
MQTTService.shared.reportSignIn(lat: loc.coordinate.latitude, lon: loc.coordinate.longitude, addr: address)
DLToast.show(text: "签到成功") {
self.dl.show(text: "签到成功") {
self.requestSignInInfo()
}
}

View File

@ -20,6 +20,8 @@ class ScanVC: BaseViewController {
view = rootView
}
private let scanDoneAction: ((String) -> Void)?
private var captureSession: AVCaptureSession!
private var previewLayer: AVCaptureVideoPreviewLayer!
private var isScanning = false
@ -51,6 +53,15 @@ class ScanVC: BaseViewController {
}
rootView.scanLineView.layer.removeAllAnimations()
}
init(scanDoneAction: ((String) -> Void)? = nil) {
self.scanDoneAction = scanDoneAction
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func handleScanResult(text: String) {
guard !isScanning else { return }
@ -58,15 +69,18 @@ class ScanVC: BaseViewController {
captureSession.stopRunning()
rootView.scanLineView.alpha = 0
rootView.scanLineView.layer.removeAllAnimations()
requestJoinGroup(code: text)
if let scanDoneAction = scanDoneAction {
scanDoneAction(text)
AppRouter.shared.popOrDismiss()
}
// requestJoinGroup(code: text)
}
// MARK: - API
private func requestJoinGroup(code: String) {
DLToast.showLoading()
dl.showLoading()
GroupService.operate(opType: "join", requestData: ["share_code" : code]).subscribe(onNext: { response in
DLToast.show(text: "申请成功") {
NotificationCenter.default.post(name: .RefreshGroupInfoNotification, object: nil)
self.dl.show(text: "申请成功") {
AppRouter.shared.popOrDismiss()
}
}, onError: { [weak self] (error) in

View File

@ -0,0 +1,447 @@
//
// CreateSchedulePopView.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
import BRPickerView
// MARK: -
struct SchedulePointItem: IdentifiableType, Equatable {
let identity: String = UUID().uuidString
var locationName: String = ""
var address: String = ""
var expectedTime: Date?
var remark: String = ""
}
typealias SchedulePointSection = SectionModel<String, SchedulePointItem>
// MARK: - CreateSchedulePopView
class CreateSchedulePopView: UIView {
var disposeBag = DisposeBag()
let pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: [SchedulePointItem()])
// MARK: - Setup
private func setupRx() {
dateLab.rx.tapGesture.subscribe { _ in
let picker = BRDatePickerView(pickerMode: .YMD)
picker.minDate = Date()
picker.maxDate = Calendar.current.date(byAdding: .day, value: 6, to: Date())
let style = BRPickerStyle()
style.selectRowTextColor = UIColor(hexStr: "#16B3FF")
picker.pickerStyle = style
picker.resultBlock = { [weak self] selectDate, value in
guard let date = selectDate else { return }
let fmt = DateFormatter()
fmt.dateFormat = "yyyy年MM月dd日"
self?.dateLab.text = fmt.string(from: date)
}
picker.show()
}.disposed(by: disposeBag)
//
addBtn.rx.tap
.subscribe(onNext: { [weak self] _ in
guard let self = self else { return }
var list = self.pointsRelay.value
list.append(SchedulePointItem(
locationName: "",
address: "",
expectedTime: nil,
remark: ""
))
self.pointsRelay.accept(list)
})
.disposed(by: disposeBag)
// tableView
pointsRelay
.observe(on: MainScheduler.asyncInstance)
.map { [SchedulePointSection(model: "", items: $0)] }
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
// tableView
pointsRelay
.subscribe(onNext: { [weak self] items in
guard let self = self else { return }
let rowHeight: CGFloat = 122
var h = CGFloat(items.count) * rowHeight
h = max(h, rowHeight)
self.tableView.layoutChain.height(h)
})
.disposed(by: disposeBag)
}
private lazy var dataSource: RxTableViewSectionedReloadDataSource<SchedulePointSection> = {
RxTableViewSectionedReloadDataSource<SchedulePointSection>(
configureCell: { [weak self] _, tv, indexPath, item in
let cell: SchedulePointCell = tv.dequeueReusableCell(for: indexPath)
cell.configure(item: item,
index: indexPath.row,
total: self?.pointsRelay.value.count ?? 0,
onDelete: { [weak self] in
guard let self = self else { return }
var list = self.pointsRelay.value
guard indexPath.row < list.count else { return }
list.remove(at: indexPath.row)
self.pointsRelay.accept(list)
})
return cell
})
}()
// MARK: - UI
private func setupUI() {
addSubview(lineView)
addSubview(dateView)
addSubview(detailView)
addSubview(scrollView)
lineView.layoutChain
.top(15)
.centerX()
.width(36)
.height(4)
dateView.layoutChain
.topToBottomOfView(lineView, offset: 19)
.edgesHorzontal()
.height(22)
detailView.layoutChain
.topToBottomOfView(dateView, offset: 20)
.edgesHorzontal()
.height(22)
scrollView.layoutChain
.topToBottomOfView(detailView, offset: 15)
.edges(excludingEdge: .top)
}
// MARK: - Views
lazy var lineView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(hexStr: "#EBEBEB")
v.cornerRadius = 2
return v
}()
lazy var dateView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let titleLab = UILabel()
titleLab.text = "行程日期:"
titleLab.font = .systemFont(ofSize: 14, weight: .medium)
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
view.addSubview(titleLab)
titleLab.layoutChain.left(15).centerY()
view.addSubview(dateLab)
dateLab.layoutChain.leftToRightOfView(titleLab).centerY()
return view
}()
lazy var dateLab: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 14, weight: .medium)
label.textColor = UIColor(hexStr: "#16B3FF")
label.isUserInteractionEnabled = true
let fmt = DateFormatter()
fmt.dateFormat = "yyyy年MM月dd日"
label.text = fmt.string(from: Date())
return label
}()
lazy var detailView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let titleLab = UILabel()
titleLab.text = "行程详情"
titleLab.font = .systemFont(ofSize: 14, weight: .medium)
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
view.addSubview(titleLab)
titleLab.layoutChain.left(15).centerY()
view.addSubview(addBtn)
addBtn.layoutChain.right(15).centerY().width(18).height(18)
return view
}()
lazy var addBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Schedule/add"), for: .normal)
btn.backgroundColor = .clear
btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 10, bottom: 10, right: 15)
return btn
}()
lazy var scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = .white
view.showsHorizontalScrollIndicator = false
view.bounces = false
let contentView = UIView()
contentView.backgroundColor = .clear
view.addSubview(contentView)
contentView.layoutChain
.edges().widthToView(view)
contentView.addSubview(tableView)
tableView.layoutChain
.top()
.edgesHorzontal()
contentView.addSubview(createBtn)
createBtn.layoutChain
.topToBottomOfView(tableView, offset: 30)
.edgesHorzontal(30)
.height(50)
.bottom(kSafeBottomMargin + 10)
return view
}()
lazy var tableView: UITableView = {
let tv = UITableView(frame: .zero, style: .plain)
tv.backgroundColor = .clear
tv.separatorStyle = .none
tv.estimatedRowHeight = 122
tv.isScrollEnabled = false
tv.showsVerticalScrollIndicator = false
tv.bounces = false
tv.register(SchedulePointCell.self)
return tv
}()
lazy var createBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("立即创建", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
btn.cornerRadius = 25
return btn
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setupUI()
setupRx()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
layoutIfNeeded()
setCornerRadius(corners: [.topLeft, .topRight], withCornerRadii: CGSize(width: 30, height: 30))
}
}
// MARK: - SchedulePointCell
class SchedulePointCell: UITableViewCell {
var disposeBag = DisposeBag()
func configure(item: SchedulePointItem, index: Int, total: Int, onDelete: @escaping () -> Void) {
disposeBag = DisposeBag()
indexLabel.text = "\(index + 1)"
locationLabel.text = item.locationName.isEmpty ? "点击选择地点" : item.locationName
if let expectedTime = item.expectedTime {
timeLabel.text = formatTime(expectedTime)
}
else {
timeLabel.text = "请选择到达时间"
}
// 线线
topDashView.isHidden = total <= 1 || index == 0
bottomDashView.isHidden = total <= 1 || index == total - 1
//
deleteBtn.rx.tap
.subscribe(onNext: { onDelete() })
.disposed(by: disposeBag)
}
private func formatTime(_ date: Date) -> String {
let fmt = DateFormatter()
fmt.dateFormat = "HH:mm"
return fmt.string(from: date)
}
// MARK: - Init
override init(style: CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
setupViews()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
private func setupViews() {
contentView.addSubview(topDashView)
contentView.addSubview(pointIcon)
contentView.addSubview(bottomDashView)
contentView.addSubview(cardView)
cardView.addSubview(cardCornerView)
cardCornerView.addSubview(indexLabel)
cardCornerView.addSubview(locationLabel)
cardCornerView.addSubview(timeLabel)
cardCornerView.addSubview(remarkTF)
cardCornerView.addSubview(deleteBtn)
topDashView.layoutChain
.top().width(2)
pointIcon.layoutChain
.centerY()
.left(22)
.width(18).height(18)
topDashView.layoutChain
.centerX(pointIcon)
.bottomToTopOfView(pointIcon, offset: -10)
bottomDashView.layoutChain
.topToBottomOfView(pointIcon, offset: 10)
.centerX(pointIcon)
.width(2)
.bottom()
//
cardView.layoutChain
.top(15)
.leftToRightOfView(pointIcon, offset: 20)
.right(15)
.height(92)
.bottom(15)
cardCornerView.layoutChain.edges()
indexLabel.layoutChain
.top().left()
.width(30).height(20)
locationLabel.layoutChain
.topToBottomOfView(indexLabel, offset: 10)
.left(10)
timeLabel.layoutChain
.centerY(locationLabel)
.leftToRightOfView(locationLabel, offset: 38)
deleteBtn.layoutChain
.bottom(15).right(15)
.width(14).height(14)
remarkTF.layoutChain
.topToBottomOfView(locationLabel, offset: 15)
.leftToView(locationLabel)
.rightToLeftOfView(deleteBtn, offset: -10)
}
// MARK: - Views
private let topDashView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(hexStr: "#E0E0E0")
v.isHidden = true
return v
}()
private let pointIcon: UIImageView = {
let iv = UIImageView(image: UIImage(named: "Schedule/point"))
iv.contentMode = .scaleAspectFit
return iv
}()
private let bottomDashView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(hexStr: "#E0E0E0")
v.isHidden = true
return v
}()
private let cardView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0)
view.layer.shadowOpacity = 1
view.layer.shadowRadius = 8
return view
}()
lazy var cardCornerView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.cornerRadius = 10
return view
}()
private let indexLabel: UILabel = {
let l = UILabel()
l.backgroundColor = UIColor(hexStr: "#DCF4FF")
l.textColor = UIColor(hexStr: "#176F9B")
l.font = .systemFont(ofSize: 12, weight: .medium)
l.textAlignment = .center
return l
}()
private let locationLabel: UILabel = {
let l = UILabel()
l.font = .systemFont(ofSize: 14, weight: .medium)
l.textColor = UIColor(hexStr: "#333333")
l.lineBreakMode = .byTruncatingTail
return l
}()
private let timeLabel: UILabel = {
let l = UILabel()
l.font = .systemFont(ofSize: 12, weight: .medium)
l.textColor = UIColor(hexStr: "#333333")
l.isUserInteractionEnabled = true
return l
}()
lazy var remarkTF: UITextField = {
let tf = UITextField()
tf.font = .systemFont(ofSize: 12)
tf.placeholder = "在此添加备注..."
tf.returnKeyType = .done
return tf
}()
private let deleteBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Schedule/delete"), for: .normal)
btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 15, right: 15)
return btn
}()
override func layoutSubviews() {
super.layoutSubviews()
cardCornerView.layoutIfNeeded()
indexLabel.setCornerRadius(corners: [.bottomRight], withCornerRadii: CGSize(width: 10, height: 10))
}
}

View File

@ -0,0 +1,36 @@
//
// CreateScheduleVC.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
class CreateScheduleVC: BaseViewController {
fileprivate var rootView: CreateScheduleView!
override func loadView() {
rootView = CreateScheduleView(frame: UIScreen.main.bounds)
view = rootView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}

View File

@ -0,0 +1,219 @@
//
// CreateScheduleView.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
import RxSwift
import RxCocoa
#if !targetEnvironment(simulator)
import AMapNaviKit
#endif
class CreateScheduleView: UIView {
var disposeBag = DisposeBag()
let createSchedulePopView = CreateSchedulePopView()
// MARK: - PopView
private var popTopConstraint: NSLayoutConstraint?
private var popUpLimit: CGFloat = 0
private var popDownLimit: CGFloat = 0
private var isLimitsSet = false
private var panStartTop: CGFloat = 0
private var isSubCanScroll = false
private func setupRx() {
backBtn.rx.tap.subscribe(onNext: { _ in
AppRouter.shared.popOrDismiss()
}).disposed(by: disposeBag)
// tableView PopView pan
createSchedulePopView.scrollView.rx.contentOffset
.subscribe(onNext: { [weak self] offset in
guard let self = self else { return }
if self.isSubCanScroll {
if offset.y <= 0 {
self.isSubCanScroll = false
self.createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
}
} else if offset.y != 0 {
self.createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
}
})
.disposed(by: disposeBag)
}
private func setupUI() {
#if !targetEnvironment(simulator)
addSubview(mapView)
#endif
addSubview(navBgView)
addSubview(navBarView)
navBarView.addSubview(backBtn)
navBarView.addSubview(navTitleLabel)
addSubview(createSchedulePopView)
navBgView.layoutChain
.edges(excludingEdge: .bottom)
.heightToWidth(160/375)
navBarView.layoutChain
.edges(excludingEdge: .bottom)
.height(kNaviHeight)
backBtn.layoutChain
.centerY(navTitleLabel)
.left(15)
.width(24).height(24)
navTitleLabel.layoutChain
.top(kStatusBarHeight + 12)
.centerX()
#if !targetEnvironment(simulator)
mapView.layoutChain
.top()
.edgesHorzontal()
.bottom()
#endif
// PopView: 1/3 navBar
createSchedulePopView.layoutChain
.edgesHorzontal()
.bottom()
.top(kScreenHeight / 3 * 2)
popTopConstraint = createSchedulePopView.jh_constraint(
.top, toAttribute: .top, otherView: createSchedulePopView.superview, relation: .equal
)
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePopPan(_:)))
pan.delegate = self
createSchedulePopView.addGestureRecognizer(pan)
}
// MARK: - Pan Gesture
@objc private func handlePopPan(_ pan: UIPanGestureRecognizer) {
guard isLimitsSet, let topConstraint = popTopConstraint else { return }
switch pan.state {
case .began:
layoutIfNeeded()
panStartTop = createSchedulePopView.frame.minY
case .changed:
let newTop = panStartTop + pan.translation(in: self).y
if isSubCanScroll {
let tableViewOffset = self.createSchedulePopView.scrollView.contentOffset.y
if tableViewOffset > 0, newTop >= popUpLimit {
return
}
isSubCanScroll = false
panStartTop = createSchedulePopView.frame.minY
}
let clamped = max(popUpLimit, min(popDownLimit, newTop))
topConstraint.constant = clamped
case .ended, .cancelled:
let velocity = pan.velocity(in: self)
let isNearUp = abs(createSchedulePopView.frame.minY - popUpLimit) < abs(createSchedulePopView.frame.minY - popDownLimit)
let target: CGFloat
if abs(velocity.y) > 200 {
target = velocity.y < 0 ? popUpLimit : popDownLimit
} else {
target = isNearUp ? popUpLimit : popDownLimit
}
topConstraint.constant = target
UIView.animate(withDuration: 0.2, delay: 0,
options: [.curveEaseInOut, .allowUserInteraction]) {
self.layoutIfNeeded()
} completion: { _ in
let atTop = target == self.popUpLimit
self.isSubCanScroll = atTop
if !atTop {
self.createSchedulePopView.scrollView.contentOffset.y = 0
}
}
default:
break
}
}
override func layoutSubviews() {
super.layoutSubviews()
if !isLimitsSet {
isLimitsSet = true
popDownLimit = kScreenHeight / 3 * 2
popUpLimit = navBarView.frame.maxY
}
}
// MARK: - Views
lazy var navBgView: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "Common/navBar_bg_2")
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var navBarView: UIView = {
let v = UIView()
v.backgroundColor = .clear
return v
}()
lazy var backBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Common/back"), for: .normal)
btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 30)
return btn
}()
lazy var navTitleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 18, weight: .medium)
label.textColor = ThemeManager.shared.color.titleAuxColor
label.text = "创建行程"
return label
}()
#if !targetEnvironment(simulator)
lazy var mapView: MAMapView = {
let mv = MAMapView()
mv.zoomLevel = 14
mv.showsUserLocation = false
mv.showsCompass = false
mv.userTrackingMode = .none
return mv
}()
#endif
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
setupUI()
setupRx()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - UIGestureRecognizerDelegate
extension CreateScheduleView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith other: UIGestureRecognizer) -> Bool {
return true
}
}

View File

@ -0,0 +1,153 @@
//
// ScheduleModel.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import ObjectMapper
import RxDataSources
//
struct ScheduleListResponse: BaseModelProtocol, ListModelType {
var pagination: PaginationModel?
//
var pageIndex = 1
//
var pageSize = 20
//
var count = 0
//
var page_count = -99
//
var code: String?
//
var message: String?
//
var list: [ScheduleModel] = []
init() {}
init?(map: Map) {
}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
list <- map["data"]
count = list.count
pagination = PaginationModel(pageIndex: pageIndex,
pageSize: pageSize,
totalNum: count,
totalPage: page_count)
}
}
struct ScheduleModel: Mappable, Equatable {
var uuid: String = UUID().uuidString
/// id
var id: String = ""
/// id
var creator_id: String = ""
///
var nick_name: String = ""
var head_pic: String = ""
///
var userIcon: UIImage {
UIImage(named: "UserIcon/\(head_pic)") ?? UIImage()
}
///
var timestamp: Int64 = 0
///
var is_follow: Bool = false
///
var is_own: Bool = false
///
var points: [SchedulePointModel] = []
///
// var group_name: String = ""
/// key
// var group_key: String = ""
init?(map: Map) {
}
mutating func mapping(map: Map) {
id <- map["id"]
creator_id <- map["creator_id"]
nick_name <- map["nick_name"]
head_pic <- map["head_pic"]
timestamp <- map["timestamp"]
is_follow <- map["is_follow"]
is_own <- map["is_own"]
points <- map["points"]
// group_name <- map["groups.group_name"]
// group_key <- map["groups.group_key"]
}
}
extension ScheduleModel: IdentifiableType {
public typealias Identity = String
public var identity: String {
return id
}
}
///
struct SchedulePointModel: Mappable, Equatable {
/// id
var id: String = ""
/// 0 1 2
var type: Int = 0
///
var expected_timestamp: Int64 = 0
///
var longitude: Double?
///
var latitude: Double?
///
var province: String = ""
var district: String = ""
var country: String = ""
var street: String = ""
var city: String = ""
var formatted_address: String = ""
///
var remark: String = ""
///
var eventText: String = ""
///
var sequence: Int = 0
init?(map: Map) {
}
mutating func mapping(map: Map) {
id <- map["id"]
type <- map["type"]
expected_timestamp <- map["expected_timestamp"]
longitude <- map["location.longitude"]
latitude <- map["location.latitude"]
remark <- map["remark"]
eventText <- map["events.text"]
province <- map["address.province"]
district <- map["address.district"]
country <- map["address.country"]
street <- map["address.street"]
city <- map["address.city"]
formatted_address <- map["address.formatted_address"]
sequence <- map["sequence"]
}
}
extension SchedulePointModel: IdentifiableType {
public typealias Identity = String
public var identity: String {
return id
}
}

View File

@ -0,0 +1,105 @@
//
// ScheduleVC.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
import MJRefresh
class ScheduleVC: BaseViewController {
fileprivate var rootView: ScheduleView!
override func loadView() {
rootView = ScheduleView(frame: UIScreen.main.bounds)
view = rootView
}
private var viewModel = ScheduleViewModel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupTableViewHeaderFooter()
bindViewModel()
reactiveAction()
requestData()
requestFollowList()
}
private func bindViewModel() {
viewModel.output.sectionedItems
.bind(to: rootView.collectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
viewModel.output.scheduleSectionedItems
.bind(to: rootView.tableView.rx.items(dataSource: tableViewDataSource))
.disposed(by: disposeBag)
viewModel.output.refreshResult.subscribe(onNext: { [weak self] (status, isEmpty) in
guard let self = self else { return }
self.dl.dismiss()
self.rootView.tableView.refresh(status: status, isEmpty: isEmpty)
}).disposed(by: disposeBag)
}
private func reactiveAction() {
}
private func setupTableViewHeaderFooter() {
// header/footer
let header = MJRefreshNormalHeader(refreshingBlock: { [weak self] in
self?.viewModel.refresh()
})
header.addFeedbackGenerator()
header.stateLabel?.isHidden = true
rootView.tableView.mj_header = header
let footer = MJRefreshAutoNormalFooter(refreshingBlock: viewModel.loadMore)
footer.isHidden = true
rootView.tableView.mj_footer = footer
}
// MARK: - API
private func requestFollowList() {
dl.showLoading()
UserService.followList().subscribe(onNext: { response in
self.dl.dismiss()
self.viewModel.loadViewedData(response.list)
}, onError: { _ in }).disposed(by: disposeBag)
}
// MARK: - API
private func requestData() {
dl.showLoading()
viewModel.refresh()
}
// MARK: - dataSource
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<ViewedListSectionModel> = {
RxCollectionViewSectionedReloadDataSource<ViewedListSectionModel> { datasource, collectionView, indexPath, model in
let cell: ViewedCell = collectionView.dequeueReusableCell(for: indexPath)
cell.configure(model)
return cell
}
}()
// MARK: - UITableViewDataSource
lazy private var tableViewDataSource: RxTableViewSectionedReloadDataSource<ScheduleListSectionModel> = {
return RxTableViewSectionedReloadDataSource<ScheduleListSectionModel>(
configureCell: { (_, tableView, indexPath, model) in
let cell: ScheduleListPopCell = tableView.dequeueReusableCell(for: indexPath)
cell.configure(model)
return cell
}
)
}()
}

View File

@ -0,0 +1,422 @@
//
// ScheduleView.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
import RxSwift
import RxCocoa
class ScheduleView: UIView {
var disposeBag = DisposeBag()
private func setupRx() {
createBtn.rx.tap.subscribe(onNext: { _ in
AppRouter.push(Route.createSchedule)
}).disposed(by: disposeBag)
}
private func setupUI() {
addSubview(navBgView)
addSubview(navBarView)
navBarView.addSubview(navTitleLabel)
addSubview(headerView)
addSubview(travelRouteView)
addSubview(tableView)
addSubview(createBtn)
navBgView.layoutChain
.edges(excludingEdge: .bottom)
.height(kNaviHeight)
navBarView.layoutChain
.edges(excludingEdge: .bottom)
.height(kNaviHeight)
navTitleLabel.layoutChain
.top(kStatusBarHeight + 12)
.centerX()
headerView.layoutChain
.topToBottomOfView(navBarView)
.edgesHorzontal()
// .height(147)
travelRouteView.layoutChain
.topToBottomOfView(headerView)
.edgesHorzontal()
tableView.layoutChain
.topToBottomOfView(travelRouteView)
.edges(excludingEdge: .top)
createBtn.layoutChain
.bottom(kSafeBottomMargin + 102)
.right(10)
.width(60)
.heightToWidth(1)
}
lazy var navBgView: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "Common/navBar_bg_1")
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var navBarView: UIView = {
let view = UIView()
view.backgroundColor = .clear
return view
}()
lazy var navTitleLabel: UILabel = {
let label = UILabel()
label.text = "行程"
label.font = .systemFont(ofSize: 18, weight: .medium)
label.textColor = ThemeManager.shared.color.titleAuxColor
label.textAlignment = .center
return label
}()
///
lazy var headerView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let titleLab = UILabel()
titleLab.text = "谁看过我"
titleLab.font = .systemFont(ofSize: 16, weight: .medium)
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
view.addSubview(titleLab)
titleLab.layoutChain
.top(15)
let dotView = UIView()
dotView.backgroundColor = UIColor(hexStr: "#16B3FF")
dotView.cornerRadius = 2
view.addSubview(dotView)
dotView.layoutChain
.left(15)
.centerY(titleLab)
.width(4)
.height(11)
titleLab.layoutChain.leftToRightOfView(dotView, offset: 5)
let historyBtn = UIButton()
historyBtn.setTitle("历史行程", for: .normal)
historyBtn.setTitleColor(ThemeManager.shared.color.titleAuxColor, for: .normal)
historyBtn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
view.addSubview(historyBtn)
historyBtn.layoutChain
.right(15)
.centerY(titleLab)
historyBtn.sizeToFit()
view.addSubview(collectionView)
collectionView.layoutChain
.topToBottomOfView(titleLab, offset: 15)
.edgesHorzontal()
.height(80)
.bottom(15)
return view
}()
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 80, height: 80)
layout.minimumLineSpacing = 15
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear
cv.showsHorizontalScrollIndicator = false
cv.register(ViewedCell.self)
return cv
}()
/// 线
lazy var travelRouteView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let titleLab = UILabel()
titleLab.text = "行程路线"
titleLab.font = .systemFont(ofSize: 16, weight: .medium)
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
view.addSubview(titleLab)
titleLab.layoutChain
.top(15)
.edgesVertical(5)
let dotView = UIView()
dotView.backgroundColor = UIColor(hexStr: "#16B3FF")
dotView.cornerRadius = 2
view.addSubview(dotView)
dotView.layoutChain
.left(15)
.centerY(titleLab)
.width(4)
.height(11)
titleLab.layoutChain.leftToRightOfView(dotView, offset: 5)
return view
}()
lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 80
tableView.register(ScheduleListPopCell.self)
return tableView
}()
lazy var createBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "Schedule/create"), for: .normal)
return btn
}()
override init(frame: CGRect) {
super.init(frame: .zero)
backgroundColor = .white
setupUI()
setupRx()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - ViewedCell
final class ViewedCell: UICollectionViewCell {
func configure(_ model: ViewedModel) {
iconView.image = model.userIcon
//
blurView.isHidden = AppContextManager.shared.vip > 1
lockView.isHidden = AppContextManager.shared.vip > 1
viewedCountLab.text = "看了\(model.count)"
}
lazy var iconView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.cornerRadius = 40
iv.clipsToBounds = true
iv.backgroundColor = .clear
iv.addSubview(blurView)
blurView.layoutChain.edges()
return iv
}()
lazy var blurView: UIVisualEffectView = {
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight)
let view = UIVisualEffectView(effect: blurEffect)
return view
}()
lazy var lockView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let lockIcon = UIImageView(image: UIImage(named: "Common/lock_white"))
lockIcon.contentMode = .scaleAspectFill
view.addSubview(lockIcon)
lockIcon.layoutChain
.top(17)
.centerX()
view.addSubview(viewedCountLab)
viewedCountLab.layoutChain
.bottom(17)
.edgesHorzontal()
return view
}()
lazy var viewedCountLab: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .medium)
label.textColor = .white
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(iconView)
contentView.addSubview(lockView)
iconView.layoutChain
.edges()
lockView.layoutChain
.edges()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - ScheduleListPopCell
class ScheduleListPopCell: UITableViewCell {
func configure(_ model: ScheduleModel) {
nameLab.text = model.nick_name + " 的行程路线"
monthLab.text = getDateInterval2String(date: "\(model.timestamp/1000)", dateFormat: "MM月")
if model.is_own {
editBtn.isHidden = false
followBtn.isHidden = true
}
else {
editBtn.isHidden = true
followBtn.isHidden = model.is_follow ? true : false
}
guard let pointModel = model.points.last else { return }
dateLab.text = getDateInterval2String(date: "\(pointModel.expected_timestamp/1000)", dateFormat: "MM.dd")
}
override init(style: CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
setupSubviews()
}
private func setupSubviews() {
contentView.addSubview(bgView)
bgView.addSubview(cornerView)
cornerView.addSubview(nameLab)
cornerView.addSubview(monthLab)
cornerView.addSubview(dateLab)
cornerView.addSubview(editBtn)
cornerView.addSubview(followBtn)
bgView.layoutChain
.edgesVertical(15)
.edgesHorzontal(15)
.height(80)
cornerView.layoutChain.edges()
monthLab.layoutChain
.bottomToCenterYOfView(cornerView)
.left(11)
dateLab.layoutChain
.topToBottomOfView(monthLab)
.leftToView(monthLab)
nameLab.layoutChain
.centerY()
.leftToRightOfView(monthLab, offset: 15)
editBtn.layoutChain
.right(10)
.centerY()
.width(56)
.height(28)
followBtn.layoutChain
.topToView(editBtn)
.rightToView(editBtn)
.width(56)
.height(28)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
lazy var bgView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0)
view.layer.shadowOpacity = 1
view.layer.shadowRadius = 8
return view
}()
lazy var cornerView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.cornerRadius = 10
return view
}()
lazy var monthLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.titleAuxColor
label.font = .systemFont(ofSize: 14, weight: .medium)
return label
}()
lazy var dateLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.subTitleColor
label.font = .systemFont(ofSize: 12, weight: .regular)
return label
}()
lazy var nameLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.titleAuxColor
label.font = .systemFont(ofSize: 14, weight: .medium)
return label
}()
lazy var editBtn: UIButton = {
let btn = UIButton()
btn.isHidden = true
btn.setTitle("编辑", for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
btn.setTitleColor(UIColor(hexStr: "#0F2846"), for: .normal)
btn.backgroundColor = UIColor(hexStr: "#EEFAFF")
btn.borderWidth = 0.5
btn.borderColor = UIColor(hexStr: "#16B3FF")
btn.cornerRadius = 14
return btn
}()
lazy var followBtn: UIButton = {
let btn = UIButton()
btn.isHidden = true
btn.setTitle("关注", for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
btn.setTitleColor(UIColor(hexStr: "#16B3FF"), for: .normal)
btn.backgroundColor = .white
btn.borderWidth = 0.5
btn.borderColor = UIColor(hexStr: "#16B3FF")
btn.cornerRadius = 14
return btn
}()
}

View File

@ -0,0 +1,63 @@
//
// ScheduleViewModel.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import RxSwift
import RxCocoa
import RxDataSources
import MJRefresh
typealias ViewedListSectionModel = SectionModel<String, ViewedModel>
typealias ScheduleListSectionModel = AnimatableSectionModel<String, ScheduleModel>
class ScheduleViewModel: ViewModelType {
struct Input {}
struct Output {
var sectionedItems: Observable<[ViewedListSectionModel]>
var scheduleSectionedItems: Observable<[ScheduleListSectionModel]>
var refreshResult: Observable<RefreshResult>
var pagination: Observable<PaginationModel?>
var error: Observable<Error>
}
let input: Input
let output: Output
private var listService: ListService<ScheduleListResponse>
private let sectionedItems = PublishSubject<[ViewedListSectionModel]>()
private let scheduleSectionedItems = PublishSubject<[ScheduleListSectionModel]>()
var refresh: MJRefreshComponentAction {
return {
self.listService.request.onNext(.refresh)
}
}
var loadMore: MJRefreshComponentAction {
return {
self.listService.request.onNext(.more)
}
}
func loadViewedData(_ list: [ViewedModel]) {
sectionedItems.onNext(list.mapSection())
}
// MARK: - init
init() {
listService = ItineraryService.query()
input = Input()
output = Output(
sectionedItems: sectionedItems.asObservable(),
scheduleSectionedItems: listService.animatableSectionedItems,
refreshResult: listService.refreshResult,
pagination: listService.pagination,
error: listService.error
)
}
}

View File

@ -0,0 +1,60 @@
//
// ViewedModel.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import ObjectMapper
import RxDataSources
///
struct ViewedListResponse: BaseModelProtocol {
//
var code: String?
//
var message: String?
//
var list: [ViewedModel] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
list <- map["data"]
}
}
struct ViewedModel: Mappable, Equatable {
var uuid: String = UUID().uuidString
///
var user_id: String = ""
var nick_name: String = ""
var head_pic: String = ""
///
var userIcon: UIImage {
UIImage(named: "UserIcon/\(head_pic)") ?? UIImage()
}
///
var count: Int = 0
init?(map: Map) {
}
mutating func mapping(map: Map) {
user_id <- map["user_id"]
nick_name <- map["nick_name"]
head_pic <- map["head_pic"]
count <- map["count"]
}
}
extension ViewedModel: IdentifiableType {
public typealias Identity = String
public var identity: String {
return user_id
}
}

View File

@ -0,0 +1,33 @@
//
// ItineraryService.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import RxSwift
import Moya
struct ItineraryService {
static let disposeBag = DisposeBag()
///
/// - Parameters:
/// - follow:
/// - own:
/// - history: true
/// - group_key:
static func query(follow: Bool=false,
own: Bool=false,
history: Bool=false,
group_key: String="") -> ListService<ScheduleListResponse> {
ListService(newPaging: true) {
ItineraryAPI.query(follow: follow,
own: own,
history: history,
group_key: group_key,
page: $0).multiTarget
}
}
}

View File

@ -33,4 +33,12 @@ struct SystemService {
.map(ResponseModel.self)
.asObservable()
}
/// SOS
static func sos(enable: Bool) -> Observable<ResponseModel> {
let api = SystemAPI.sos(enable: enable).multiTarget
return APIProvider.request(token: api)
.map(ResponseModel.self)
.asObservable()
}
}

Some files were not shown because too many files have changed in this diff Show More