initial commit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// FUAlertController.h
|
||||
//
|
||||
// Created by 项林平 on 2019/6/21.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FUAlertController;
|
||||
|
||||
@interface FUAlertModel : NSObject
|
||||
@property (nonatomic, copy) NSString *alertTitle;
|
||||
@property (nonatomic, copy) NSString *alertMessage;
|
||||
@property (nonatomic, assign) UIAlertControllerStyle preferredStyle;
|
||||
|
||||
- (instancetype) initWithTitle:(NSString *)title message:(NSString *)message style:(UIAlertControllerStyle)style;
|
||||
|
||||
@end
|
||||
|
||||
typedef void (^FUAlert)(FUAlertController *controller);
|
||||
typedef FUAlertController * _Nonnull (^FUShowAlert)(UIViewController *controller);
|
||||
typedef FUAlertController * _Nonnull (^FUActions)(NSArray<UIAlertAction *> *actions);
|
||||
typedef FUAlertController * _Nullable (^FUSourceView)(UIView *sourceView);
|
||||
|
||||
@interface FUAlertController : UIAlertController
|
||||
|
||||
+ (FUAlertController *)makeAlert:(FUAlert)block alertModel:(FUAlertModel *)model;
|
||||
|
||||
/**
|
||||
设置Actions
|
||||
|
||||
@return Self
|
||||
*/
|
||||
- (FUActions)actionItems;
|
||||
|
||||
/**
|
||||
当设备为iPad时设置SourceView
|
||||
|
||||
@return Self
|
||||
*/
|
||||
- (FUSourceView)sourceView;
|
||||
|
||||
/**
|
||||
显示Alert
|
||||
|
||||
@return Self
|
||||
*/
|
||||
- (FUShowAlert)showAlert;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// FUAlertController.m
|
||||
//
|
||||
// Created by 项林平 on 2019/6/21.
|
||||
//
|
||||
|
||||
#import "FUAlertController.h"
|
||||
|
||||
@implementation FUAlertModel
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message style:(UIAlertControllerStyle)style {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.alertTitle = title;
|
||||
self.alertMessage = message;
|
||||
self.preferredStyle = style;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FUAlertController
|
||||
+(FUAlertController *)makeAlert:(FUAlert)block alertModel:(FUAlertModel *)model {
|
||||
FUAlertController *alert = [FUAlertController alertControllerWithTitle:model.alertTitle message:model.alertMessage preferredStyle:model.preferredStyle];
|
||||
block(alert);
|
||||
return alert;
|
||||
}
|
||||
- (FUActions)actionItems {
|
||||
FUActions actionsBlock = ^(NSArray<UIAlertAction *> *actions) {
|
||||
[actions enumerateObjectsUsingBlock:^(UIAlertAction * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self addAction:obj];
|
||||
}];
|
||||
return self;
|
||||
};
|
||||
return actionsBlock;
|
||||
}
|
||||
- (FUShowAlert)showAlert {
|
||||
FUShowAlert showBlock = ^(UIViewController *controller) {
|
||||
[controller presentViewController:self animated:YES completion:nil];
|
||||
return self;
|
||||
};
|
||||
return showBlock;
|
||||
}
|
||||
- (FUSourceView)sourceView {
|
||||
FUSourceView sourceViewBlock = ^(UIView *sourceView) {
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad && sourceView) {
|
||||
self.popoverPresentationController.sourceView = sourceView;
|
||||
self.popoverPresentationController.sourceRect = sourceView.bounds;
|
||||
self.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny;
|
||||
}
|
||||
return self;
|
||||
};
|
||||
return sourceViewBlock;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// FUAlertManager.h
|
||||
//
|
||||
// Created by 项林平 on 2019/9/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUAlertManager : NSObject
|
||||
|
||||
/// Alert
|
||||
/// @param titleString title
|
||||
/// @param messageString message
|
||||
/// @param cancelString 取消
|
||||
/// @param confirmString 确定
|
||||
/// @param viewController 控制器
|
||||
/// @param confirm 确定闭包
|
||||
/// @param cancel 取消闭包
|
||||
+ (void)showAlertWithTitle:(nullable NSString *)titleString
|
||||
message:(nullable NSString *)messageString
|
||||
cancel:(nullable NSString *)cancelString
|
||||
confirm:(nullable NSString *)confirmString
|
||||
inController:(nullable UIViewController *)viewController
|
||||
confirmHandler:(nullable void (^)(void))confirm
|
||||
cancelHandler:(nullable void (^)(void))cancel;
|
||||
|
||||
/// ActionSheet
|
||||
/// @param titleString title
|
||||
/// @param messageString message
|
||||
/// @param cancelString 取消
|
||||
/// @param viewController 控制器
|
||||
/// @param sourceView 设备为iPad时需要传入
|
||||
/// @param actions 选项
|
||||
/// @param actionHandler 选项闭包
|
||||
+ (void)showActionSheetWithTitle:(nullable NSString *)titleString
|
||||
message:(nullable NSString *)messageString
|
||||
cancel:(nullable NSString *)cancelString
|
||||
inController:(nullable UIViewController *)viewController
|
||||
sourceView:(nullable UIView *)sourceView
|
||||
actions:(NSArray<NSString *> *)actions
|
||||
actionHandler:(nullable void (^)(NSInteger index))actionHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// FUAlertManager.m
|
||||
//
|
||||
// Created by 项林平 on 2019/9/25.
|
||||
//
|
||||
|
||||
#import "FUAlertManager.h"
|
||||
#import "FUAlertController.h"
|
||||
|
||||
@implementation FUAlertManager
|
||||
|
||||
+ (void)showAlertWithTitle:(NSString *)titleString
|
||||
message:(NSString *)messageString
|
||||
cancel:(NSString *)cancelString
|
||||
confirm:(NSString *)confirmString
|
||||
inController:(UIViewController *)viewController
|
||||
confirmHandler:(void (^)(void))confirm
|
||||
cancelHandler:(void (^)(void))cancel {
|
||||
if (!cancelString && !confirmString) {
|
||||
return;
|
||||
}
|
||||
FUAlertModel *alertModel = [[FUAlertModel alloc] initWithTitle:titleString message:messageString style:UIAlertControllerStyleAlert];
|
||||
__block UIViewController *currentViewController = viewController;
|
||||
[FUAlertController makeAlert:^(FUAlertController * _Nonnull controller) {
|
||||
NSMutableArray *items = [[NSMutableArray alloc] init];
|
||||
if (cancelString) {
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelString style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
if (cancel) {
|
||||
cancel();
|
||||
}
|
||||
}];
|
||||
//设置Action文字颜色
|
||||
[cancelAction setValue:[UIColor colorWithRed:44/255.0 green:46/255.0 blue:48/255.0 alpha:1.0] forKey:@"titleTextColor"];
|
||||
[items addObject:cancelAction];
|
||||
}
|
||||
|
||||
if (confirmString) {
|
||||
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:confirmString style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
|
||||
if (confirm) {
|
||||
confirm();
|
||||
}
|
||||
}];
|
||||
[confirmAction setValue:[UIColor colorWithRed:103/255.f green:195/255.f blue:103/255.f alpha:1] forKey:@"titleTextColor"];
|
||||
[items addObject:confirmAction];
|
||||
}
|
||||
if (!currentViewController) {
|
||||
currentViewController = [self topViewController];
|
||||
}
|
||||
if (!currentViewController) {
|
||||
NSLog(@"FUAlert: viewController can not be nil!");
|
||||
return;
|
||||
}
|
||||
controller.actionItems(items).showAlert(currentViewController);
|
||||
} alertModel:alertModel];
|
||||
}
|
||||
+ (void)showActionSheetWithTitle:(NSString *)titleString
|
||||
message:(NSString *)messageString
|
||||
cancel:(NSString *)cancelString
|
||||
inController:(UIViewController *)viewController
|
||||
sourceView:(UIView *)sourceView
|
||||
actions:(NSArray<NSString *> *)actions
|
||||
actionHandler:(void (^)(NSInteger))actionHandler {
|
||||
if (actions.count == 0) {
|
||||
return;
|
||||
}
|
||||
FUAlertModel *alertModel = [[FUAlertModel alloc] initWithTitle:titleString message:messageString style:UIAlertControllerStyleActionSheet];
|
||||
__block UIViewController *currentViewController = viewController;
|
||||
[FUAlertController makeAlert:^(FUAlertController * _Nonnull controller) {
|
||||
NSMutableArray *items = [[NSMutableArray alloc] init];
|
||||
if (cancelString) {
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelString style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
|
||||
}];
|
||||
[items addObject:cancelAction];
|
||||
}
|
||||
[actions enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
UIAlertAction *alertAction = [UIAlertAction actionWithTitle:obj style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
if (actionHandler) {
|
||||
actionHandler(idx);
|
||||
}
|
||||
}];
|
||||
[items addObject:alertAction];
|
||||
}];
|
||||
if (!currentViewController) {
|
||||
currentViewController = [self topViewController];
|
||||
}
|
||||
if (!currentViewController) {
|
||||
NSLog(@"FUAlert: viewController can not be nil!");
|
||||
return;
|
||||
}
|
||||
controller.actionItems(items).sourceView(sourceView).showAlert(currentViewController);
|
||||
} alertModel:alertModel];
|
||||
}
|
||||
|
||||
+ (UIViewController *)topViewController {
|
||||
UIViewController *root = [UIApplication sharedApplication].delegate.window.rootViewController;
|
||||
return [self currentViewControllerWithRootViewController:root];
|
||||
}
|
||||
|
||||
+ (UIViewController *)currentViewControllerWithRootViewController:(UIViewController *)viewController {
|
||||
if (viewController.presentedViewController) {
|
||||
return [self currentViewControllerWithRootViewController:viewController.presentedViewController];
|
||||
} else if ([viewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigation = (UINavigationController *)viewController;
|
||||
return [self currentViewControllerWithRootViewController:navigation.visibleViewController];
|
||||
} else if ([viewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBar = (UITabBarController *)viewController;
|
||||
return [self currentViewControllerWithRootViewController:tabBar.selectedViewController];
|
||||
} else {
|
||||
return viewController;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// FUCommonUIComponent.h
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/17.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FUSegmentBar.h"
|
||||
#import "FUTipHUD.h"
|
||||
#import "FUInsetsLabel.h"
|
||||
#import "FUSlider.h"
|
||||
#import "FUItemsView.h"
|
||||
#import "FUItemCell.h"
|
||||
#import "FUSquareButton.h"
|
||||
#import "FUAlertManager.h"
|
||||
|
||||
|
||||
//! Project version number for FUCommonUIComponent.
|
||||
FOUNDATION_EXPORT double FUCommonUIComponentVersionNumber;
|
||||
|
||||
//! Project version string for FUCommonUIComponent.
|
||||
FOUNDATION_EXPORT const unsigned char FUCommonUIComponentVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <FUCommonUIComponent/PublicHeader.h>
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// FUCommonUIDefine.h
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/20.
|
||||
//
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifndef FUCommonUIDefine_h
|
||||
#define FUCommonUIDefine_h
|
||||
|
||||
static inline UIImage * FUCommonUIImageNamed(NSString *name) {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"FUCommonUIComponent" ofType:@"framework" inDirectory:@"Frameworks"];
|
||||
NSBundle *bundle = [NSBundle bundleWithPath:path];
|
||||
return [UIImage imageNamed:name inBundle:bundle compatibleWithTraitCollection:nil];;
|
||||
}
|
||||
|
||||
#endif /* FUCommonUIDefine_h */
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// FUInsetsLabel.h
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/20.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUInsetsLabel : UILabel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame insets:(UIEdgeInsets)insets;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// FUInsetsLabel.m
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/20.
|
||||
//
|
||||
|
||||
#import "FUInsetsLabel.h"
|
||||
|
||||
@interface FUInsetsLabel ()
|
||||
|
||||
@property (nonatomic) UIEdgeInsets insets;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUInsetsLabel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
return [self initWithFrame:frame insets:UIEdgeInsetsMake(8, 8, 8, 8)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame insets:(UIEdgeInsets)insets {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.insets = insets;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawTextInRect:(CGRect)rect {
|
||||
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.insets)];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
fitSize.width += self.insets.left + self.insets.right;
|
||||
fitSize.height += self.insets.top + self.insets.bottom;
|
||||
return fitSize;
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
CGSize contentSize = [super intrinsicContentSize];
|
||||
contentSize.width += self.insets.left + self.insets.right;
|
||||
contentSize.height += self.insets.top + self.insets.bottom;
|
||||
return contentSize;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// FUItemCell.h
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/24.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUItemCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, strong, readonly) UIImageView *imageView;
|
||||
|
||||
@property (nonatomic, strong, readonly) UIActivityIndicatorView *indicatorView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// FUItemCell.m
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/24.
|
||||
//
|
||||
|
||||
#import "FUItemCell.h"
|
||||
|
||||
@interface FUItemCell ()
|
||||
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *indicatorView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUItemCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self.contentView addSubview:self.imageView];
|
||||
NSLayoutConstraint *imageLeading = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *imageTrailing = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *imageTop = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *imageBottom = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
|
||||
[self.contentView addConstraints:@[imageLeading, imageTrailing, imageTop, imageBottom]];
|
||||
|
||||
[self.contentView addSubview:self.indicatorView];
|
||||
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:self.indicatorView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
|
||||
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self.indicatorView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
|
||||
[self.contentView addConstraints:@[centerXConstraint, centerYConstraint]];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setSelected:(BOOL)selected {
|
||||
[super setSelected:selected];
|
||||
self.imageView.layer.borderWidth = selected ? 3.f : 0;
|
||||
self.imageView.layer.borderColor = selected ? [UIColor colorWithRed:103/255.f green:195/255.f blue:103/255.f alpha:1].CGColor : [UIColor clearColor].CGColor;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIImageView *)imageView {
|
||||
if (!_imageView) {
|
||||
_imageView = [[UIImageView alloc] init];
|
||||
_imageView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_imageView.layer.masksToBounds = YES;
|
||||
_imageView.layer.cornerRadius = 30.f;
|
||||
}
|
||||
return _imageView;
|
||||
}
|
||||
|
||||
- (UIActivityIndicatorView *)indicatorView {
|
||||
if (!_indicatorView) {
|
||||
_indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
_indicatorView.hidden = YES;
|
||||
_indicatorView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
}
|
||||
return _indicatorView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// FUItemsView.h
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/24.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class FUItemsView;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FUItemsViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)itemsView:(FUItemsView *)itemsView didSelectItemAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface FUItemsView : UIView
|
||||
|
||||
/// 数据源
|
||||
/// @discussion 外部传入的icon名数组,查找不到icon会展示空白
|
||||
@property (nonatomic, copy) NSArray<NSString *> *items;
|
||||
|
||||
/// 当前选中索引
|
||||
/// @discussion 默认为 -1,-1为取消选中
|
||||
@property (nonatomic, assign) NSInteger selectedIndex;
|
||||
|
||||
@property (nonatomic, weak) id<FUItemsViewDelegate> delegate;
|
||||
|
||||
/// 初始化
|
||||
/// @param frame Frame
|
||||
/// @param topSpacing 顶部预留空间,默认为0
|
||||
- (instancetype)initWithFrame:(CGRect)frame topSpacing:(CGFloat)topSpacing;
|
||||
|
||||
/// 开始当前选中项动画
|
||||
/// @note 开始动画后无法选择其他项
|
||||
- (void)startAnimation;
|
||||
|
||||
/// 结束当前选中项动画
|
||||
/// @note 结束动画后可以继续选择其他项
|
||||
- (void)stopAnimation;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// FUItemsView.m
|
||||
// FUCommonUIComponent
|
||||
//
|
||||
// Created by 项林平 on 2022/6/24.
|
||||
//
|
||||
|
||||
#import "FUItemsView.h"
|
||||
#import "FUItemCell.h"
|
||||
|
||||
static NSString * const kFUItemCellIdentifier = @"FUItemCell";
|
||||
|
||||
@interface FUItemsView ()<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
|
||||
@property (nonatomic, assign) CGFloat collectionTopConstant;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUItemsView
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame topSpacing:(CGFloat)topSpacing {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.collectionTopConstant = topSpacing;
|
||||
[self configureUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - UI
|
||||
|
||||
- (void)configureUI {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
|
||||
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
|
||||
effectView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addSubview:effectView];
|
||||
NSLayoutConstraint *effectLeading = [NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *effectTrailing = [NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *effectTop = [NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *effectBottom = [NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
|
||||
[self addConstraints:@[effectLeading, effectTrailing, effectTop, effectBottom]];
|
||||
|
||||
[self addSubview:self.collectionView];
|
||||
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:self.collectionView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:self.collectionView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
|
||||
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.collectionView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.collectionTopConstant];
|
||||
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self.collectionView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:84];
|
||||
[self addConstraints:@[leading, trailing, top]];
|
||||
[self.collectionView addConstraint:height];
|
||||
|
||||
_selectedIndex = -1;
|
||||
}
|
||||
|
||||
#pragma mark - Instance methods
|
||||
|
||||
- (void)startAnimation {
|
||||
if (_selectedIndex < 0 && _selectedIndex >= self.items.count) {
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
FUItemCell *selectedCell = (FUItemCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0]];
|
||||
if (selectedCell) {
|
||||
[selectedCell.indicatorView startAnimating];
|
||||
self.collectionView.userInteractionEnabled = NO;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopAnimation {
|
||||
if (_selectedIndex < 0 && _selectedIndex >= self.items.count) {
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
FUItemCell *selectedCell = (FUItemCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0]];
|
||||
if (selectedCell) {
|
||||
[selectedCell.indicatorView stopAnimating];
|
||||
self.collectionView.userInteractionEnabled = YES;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
- (UIImage *)imageWithImageName:(NSString *)imageName {
|
||||
UIImage *resultImage = [UIImage imageNamed:imageName];
|
||||
if (!resultImage) {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"];
|
||||
resultImage = [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
if (!resultImage) {
|
||||
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]];
|
||||
resultImage = [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
return resultImage;
|
||||
}
|
||||
|
||||
#pragma mark - Collection view data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.items.count;
|
||||
}
|
||||
|
||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kFUItemCellIdentifier forIndexPath:indexPath];
|
||||
NSString *item = self.items[indexPath.item];
|
||||
cell.imageView.image = [self imageWithImageName:item];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Collection view delegate
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.item == _selectedIndex) {
|
||||
return;
|
||||
}
|
||||
_selectedIndex = indexPath.item;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(itemsView:didSelectItemAtIndex:)]) {
|
||||
[self.delegate itemsView:self didSelectItemAtIndex:indexPath.item];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Collection view delegate flow layout
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return CGSizeMake(60, 60);
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
|
||||
return UIEdgeInsetsMake(12, 16, 12, 16);
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setItems:(NSArray<NSString *> *)items {
|
||||
_items = items;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.collectionView reloadData];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setSelectedIndex:(NSInteger)selectedIndex {
|
||||
if (selectedIndex < 0 || selectedIndex >= self.items.count) {
|
||||
return;
|
||||
}
|
||||
_selectedIndex = selectedIndex;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:selectedIndex inSection:0] animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(itemsView:didSelectItemAtIndex:)]) {
|
||||
[self.delegate itemsView:self didSelectItemAtIndex:selectedIndex];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UICollectionView *)collectionView {
|
||||
if (!_collectionView) {
|
||||
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:flowLayout];
|
||||
_collectionView.backgroundColor = [UIColor clearColor];
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
[_collectionView registerClass:[FUItemCell class] forCellWithReuseIdentifier:kFUItemCellIdentifier];
|
||||
}
|
||||
return _collectionView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// FUSegmentBar.h
|
||||
// FULiveDemo
|
||||
//
|
||||
// Created by 项林平 on 2021/9/26.
|
||||
// Copyright © 2021 FaceUnity. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class FUSegmentBar;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FUSegmentBarDelegate <NSObject>
|
||||
|
||||
- (void)segmentBar:(FUSegmentBar *)segmentBar didSelectItemAtIndex:(NSUInteger)index;
|
||||
|
||||
@optional
|
||||
- (BOOL)segmentBar:(FUSegmentBar *)segmentBar shouldSelectItemAtIndex:(NSInteger)index;
|
||||
|
||||
- (BOOL)segmentBar:(FUSegmentBar *)segmentBar shouldDisableItemAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface FUSegmentBarConfigurations : NSObject
|
||||
|
||||
/// 普通颜色
|
||||
@property (nonatomic, strong) UIColor *normalTitleColor;
|
||||
/// 选中状态颜色
|
||||
@property (nonatomic, strong) UIColor *selectedTitleColor;
|
||||
/// 无法选中状态颜色
|
||||
@property (nonatomic, strong) UIColor *disabledTitleColor;
|
||||
/// 字体
|
||||
@property (nonatomic, strong) UIFont *titleFont;
|
||||
|
||||
@end
|
||||
|
||||
@interface FUSegmentBar : UIView
|
||||
|
||||
@property (nonatomic, weak) id<FUSegmentBarDelegate> delegate;
|
||||
|
||||
/// 当前选中项索引
|
||||
/// @discussion 默认为-1,-1为取消选中
|
||||
@property (nonatomic, assign, readonly) NSInteger selectedIndex;
|
||||
|
||||
/// Unavailable initializer
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
|
||||
/// 初始化
|
||||
/// @param frame frame
|
||||
/// @param titles SegmentsTitle数组
|
||||
/// @param configuration 配置信息
|
||||
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray<NSString *> *)titles configuration:(nullable FUSegmentBarConfigurations *)configuration;
|
||||
|
||||
/// 选中指定索引项
|
||||
/// @param index 索引
|
||||
- (void)selectItemAtIndex:(NSInteger)index;
|
||||
|
||||
- (void)refresh;
|
||||
|
||||
@end
|
||||
|
||||
@interface FUSegmentsBarCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *segmentTitleLabel;
|
||||
@property (nonatomic, strong) UIColor *segmentNormalTitleColor;
|
||||
@property (nonatomic, strong) UIColor *segmentSelectedTitleColor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// FUSegmentBar.m
|
||||
// FULiveDemo
|
||||
//
|
||||
// Created by 项林平 on 2021/9/26.
|
||||
// Copyright © 2021 FaceUnity. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FUSegmentBar.h"
|
||||
|
||||
@interface FUSegmentBar ()
|
||||
|
||||
@property (nonatomic, assign) NSInteger selectedIndex;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUSegmentBarConfigurations
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// 默认选中/未选中颜色
|
||||
self.selectedTitleColor = [UIColor colorWithRed:103/255.0f green:195/255.0f blue:103/255.0f alpha:1.0];
|
||||
self.normalTitleColor = [UIColor whiteColor];
|
||||
self.disabledTitleColor = [UIColor colorWithWhite:1 alpha:0.6];
|
||||
self.titleFont = [UIFont systemFontOfSize:13];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static NSString * const kFUSegmentCellIdentifierKey = @"FUSegmentCellIdentifier";
|
||||
|
||||
@interface FUSegmentBar ()<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
|
||||
@property (nonatomic, strong) FUSegmentBarConfigurations *configuration;
|
||||
@property (nonatomic, strong) NSMutableArray *titles;
|
||||
|
||||
/// cell宽度数组
|
||||
@property (nonatomic, copy) NSArray *itemWidths;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUSegmentBar
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray<NSString *> *)titles configuration:(FUSegmentBarConfigurations *)configuration {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor colorWithRed:5/255.0 green:15/255.0 blue:20/255.0 alpha:1.0];
|
||||
self.titles = [titles mutableCopy];
|
||||
self.configuration = configuration;
|
||||
if (!self.configuration) {
|
||||
self.configuration = [[FUSegmentBarConfigurations alloc] init];
|
||||
}
|
||||
|
||||
// 计算宽度
|
||||
NSMutableArray *tempWidths = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
if (self.titles.count < 7) {
|
||||
// 平均分配宽度
|
||||
CGFloat width = CGRectGetWidth(frame) / self.titles.count * 1.0;
|
||||
for (NSInteger i = 0; i < self.titles.count; i++) {
|
||||
[tempWidths addObject:@(width)];
|
||||
}
|
||||
} else {
|
||||
// 根据文字适配宽度
|
||||
for (NSString *title in self.titles) {
|
||||
CGSize nameSize = [title sizeWithAttributes:@{NSFontAttributeName : self.configuration.titleFont}];
|
||||
[tempWidths addObject:@(nameSize.width + 20)];
|
||||
}
|
||||
}
|
||||
self.itemWidths = [tempWidths copy];
|
||||
|
||||
_selectedIndex = -1;
|
||||
|
||||
[self addSubview:self.collectionView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Instance methods
|
||||
|
||||
- (void)selectItemAtIndex:(NSInteger)index {
|
||||
NSInteger count = self.titles.count;
|
||||
if (index >= count) {
|
||||
return;
|
||||
}
|
||||
if (self.selectedIndex == index) {
|
||||
// 目标索引和当前选中索引相同时,不需要处理界面逻辑,只要触发回调即可
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(segmentBar:didSelectItemAtIndex:)]) {
|
||||
[self.delegate segmentBar:self didSelectItemAtIndex:index];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (index == -1) {
|
||||
// 取消选中时只需要更新界面
|
||||
[self.collectionView deselectItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0] animated:NO];
|
||||
self.selectedIndex = -1;
|
||||
} else {
|
||||
// 正常选中需要处理界面逻辑并触发回调
|
||||
[self.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(segmentBar:didSelectItemAtIndex:)]) {
|
||||
[self.delegate segmentBar:self didSelectItemAtIndex:index];
|
||||
}
|
||||
self.selectedIndex = index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)refresh {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.collectionView reloadData];
|
||||
if (self.selectedIndex >= 0) {
|
||||
[self.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0] animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Collection view data source
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.titles.count;
|
||||
}
|
||||
|
||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUSegmentsBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kFUSegmentCellIdentifierKey forIndexPath:indexPath];
|
||||
cell.segmentTitleLabel.text = self.titles[indexPath.item];
|
||||
cell.segmentTitleLabel.font = self.configuration.titleFont;
|
||||
cell.segmentNormalTitleColor = self.configuration.normalTitleColor;
|
||||
cell.segmentSelectedTitleColor = self.configuration.selectedTitleColor;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(segmentBar:shouldDisableItemAtIndex:)]) {
|
||||
if ([self.delegate segmentBar:self shouldDisableItemAtIndex:indexPath.item]) {
|
||||
cell.segmentTitleLabel.textColor = self.configuration.disabledTitleColor;
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Collection view delegate
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
_selectedIndex = indexPath.item;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(segmentBar:didSelectItemAtIndex:)]) {
|
||||
[self.delegate segmentBar:self didSelectItemAtIndex:indexPath.item];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(segmentBar:shouldSelectItemAtIndex:)]) {
|
||||
return [self.delegate segmentBar:self shouldSelectItemAtIndex:indexPath.item];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Collection view delegate flow layout
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return CGSizeMake([self.itemWidths[indexPath.item] floatValue], CGRectGetHeight(collectionView.frame));
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
|
||||
return UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UICollectionView *)collectionView {
|
||||
if (!_collectionView) {
|
||||
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), 49.f) collectionViewLayout:flowLayout];
|
||||
_collectionView.backgroundColor = [UIColor clearColor];
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.showsVerticalScrollIndicator = NO;
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
|
||||
[_collectionView registerClass:[FUSegmentsBarCell class] forCellWithReuseIdentifier:kFUSegmentCellIdentifierKey];
|
||||
}
|
||||
return _collectionView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUSegmentsBarCell
|
||||
|
||||
#pragma mark - Initializer
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
[self.contentView addSubview:self.segmentTitleLabel];
|
||||
NSLayoutConstraint *titleLabelCenterX = [NSLayoutConstraint constraintWithItem:self.segmentTitleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
|
||||
NSLayoutConstraint *titleLabelCenterY = [NSLayoutConstraint constraintWithItem:self.segmentTitleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
|
||||
[self.contentView addConstraints:@[titleLabelCenterX, titleLabelCenterY]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setSelected:(BOOL)selected {
|
||||
[super setSelected:selected];
|
||||
if (selected) {
|
||||
self.segmentTitleLabel.textColor = self.segmentSelectedTitleColor ? self.segmentSelectedTitleColor : [UIColor colorWithRed:103/255.0f green:195/255.0f blue:103/255.0f alpha:1.0];
|
||||
} else {
|
||||
self.segmentTitleLabel.textColor = self.segmentNormalTitleColor ? self.segmentNormalTitleColor : [UIColor whiteColor];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UILabel *)segmentTitleLabel {
|
||||
if (!_segmentTitleLabel) {
|
||||
_segmentTitleLabel = [[UILabel alloc] initWithFrame:self.contentView.bounds];
|
||||
_segmentTitleLabel.textColor = [UIColor whiteColor];
|
||||
_segmentTitleLabel.font = [UIFont systemFontOfSize:13];
|
||||
_segmentTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_segmentTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
}
|
||||
return _segmentTitleLabel;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// FUSlider.h
|
||||
// FUAPIDemoBar
|
||||
//
|
||||
// Created by L on 2018/6/27.
|
||||
// Copyright © 2018年 L. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface FUSlider : UISlider
|
||||
|
||||
/// 零点是否在中间,默认为NO
|
||||
@property (nonatomic, assign, getter=isBidirection) BOOL bidirection;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// FUSlider.m
|
||||
// FUAPIDemoBar
|
||||
//
|
||||
// Created by L on 2018/6/27.
|
||||
// Copyright © 2018年 L. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FUSlider.h"
|
||||
#import "FUCommonUIDefine.h"
|
||||
|
||||
@interface FUSlider ()
|
||||
|
||||
/// 当前值提示标签
|
||||
@property (nonatomic, strong) UILabel *tipLabel;
|
||||
/// 当前值提示标签背景
|
||||
@property (nonatomic, strong) UIImageView *tipBackgroundImageView;
|
||||
/// 零点在中间时自定义视图
|
||||
@property (nonatomic, strong) UIView *trackView;
|
||||
/// 零点在中间时的中间短线
|
||||
@property (nonatomic, strong) UIView *middleLine;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUSlider
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
[self configureUI];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self configureUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView *)sliderTrackBackView {
|
||||
return self.subviews.firstObject;
|
||||
}
|
||||
|
||||
- (void)configureUI {
|
||||
[self setThumbImage:FUCommonUIImageNamed(@"slider_dot") forState:UIControlStateNormal];
|
||||
[self setMaximumTrackTintColor:[UIColor whiteColor]];
|
||||
[self setMinimumTrackTintColor:[UIColor colorWithRed:103/255.0 green:195/255.0 blue:103/255.0 alpha:1]];
|
||||
[self addSubview:self.tipBackgroundImageView];
|
||||
[self addSubview:self.tipLabel];
|
||||
[self.sliderTrackBackView addSubview:self.trackView];
|
||||
[self addSubview:self.middleLine];
|
||||
}
|
||||
|
||||
-(void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
if (!self.trackView.hidden) {
|
||||
[self.sliderTrackBackView insertSubview:self.trackView atIndex:self.subviews.count-2];
|
||||
// [self bringSubviewToFront:self.trackView];
|
||||
}
|
||||
if (!self.middleLine.hidden) {
|
||||
self.middleLine.frame = CGRectMake(CGRectGetWidth(self.bounds)/2.0 - 1, CGRectGetHeight(self.bounds)/2.0 - 4, 2, 8);
|
||||
}
|
||||
[self setValue:self.value animated:NO];
|
||||
}
|
||||
|
||||
- (void)setBidirection:(BOOL)bidirection {
|
||||
_bidirection = bidirection;
|
||||
if (bidirection) {
|
||||
self.middleLine.hidden = NO;
|
||||
self.trackView.hidden = NO;
|
||||
[self setMinimumTrackTintColor:[UIColor whiteColor]];
|
||||
} else {
|
||||
self.middleLine.hidden = YES;
|
||||
self.trackView.hidden = YES;
|
||||
[self setMinimumTrackTintColor:[UIColor colorWithRed:103/255.0 green:195/255.0 blue:103/255.0 alpha:1]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValue:(float)value animated:(BOOL)animated {
|
||||
[super setValue:value animated:animated];
|
||||
|
||||
if (_bidirection) {
|
||||
self.tipLabel.text = [NSString stringWithFormat:@"%d",(int)(value * 100 - 50)];
|
||||
CGFloat currentValue = value - 0.5;
|
||||
CGFloat width = currentValue * CGRectGetWidth(self.bounds);
|
||||
if (width < 0 ) {
|
||||
width = -width;
|
||||
}
|
||||
CGFloat originX = currentValue > 0 ? CGRectGetWidth(self.bounds) / 2.0 : CGRectGetWidth(self.bounds) / 2.0 - width;
|
||||
self.trackView.frame = CGRectMake(originX, CGRectGetHeight(self.frame)/2.0 - 2, width, 4.0);
|
||||
} else {
|
||||
self.tipLabel.text = [NSString stringWithFormat:@"%d",(int)(value * 100)];
|
||||
}
|
||||
CGFloat x = value * (self.frame.size.width - 16) - self.tipLabel.frame.size.width * 0.5 + 8;
|
||||
CGRect frame = self.tipLabel.frame;
|
||||
frame.origin.x = x;
|
||||
|
||||
self.tipBackgroundImageView.frame = frame;
|
||||
self.tipLabel.frame = frame;
|
||||
self.tipLabel.hidden = !self.isTouchInside;
|
||||
self.tipBackgroundImageView.hidden = !self.isTouchInside;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIImageView *)tipBackgroundImageView {
|
||||
if (!_tipBackgroundImageView) {
|
||||
UIImage *bgImage = FUCommonUIImageNamed(@"slider_tip_background");
|
||||
_tipBackgroundImageView = [[UIImageView alloc] initWithImage:bgImage];
|
||||
_tipBackgroundImageView.frame = CGRectMake(0, -bgImage.size.height, bgImage.size.width, bgImage.size.height);
|
||||
_tipBackgroundImageView.hidden = YES;
|
||||
}
|
||||
return _tipBackgroundImageView;
|
||||
}
|
||||
- (UILabel *)tipLabel {
|
||||
if (!_tipLabel) {
|
||||
_tipLabel = [[UILabel alloc] initWithFrame:self.tipBackgroundImageView.frame];
|
||||
_tipLabel.textColor = [UIColor whiteColor];
|
||||
_tipLabel.font = [UIFont systemFontOfSize:10 weight:UIFontWeightMedium];
|
||||
_tipLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_tipLabel.hidden = YES;
|
||||
}
|
||||
return _tipLabel;
|
||||
}
|
||||
|
||||
- (UIView *)trackView {
|
||||
if (!_trackView) {
|
||||
_trackView = [[UIView alloc] init];
|
||||
_trackView.backgroundColor = [UIColor colorWithRed:103/255.f green:195/255.f blue:103/255.f alpha:1];
|
||||
_trackView.hidden = YES;
|
||||
}
|
||||
return _trackView;
|
||||
}
|
||||
|
||||
- (UIView *)middleLine {
|
||||
if (!_middleLine) {
|
||||
_middleLine = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.bounds)/2.0 - 1, CGRectGetHeight(self.bounds)/2.0 - 4, 2, 8)];
|
||||
_middleLine.backgroundColor = [UIColor whiteColor];
|
||||
_middleLine.layer.masksToBounds = YES;
|
||||
_middleLine.layer.cornerRadius = 1.0;
|
||||
_middleLine.hidden = YES;
|
||||
}
|
||||
return _middleLine;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// FUSquareButton.h
|
||||
// FULive
|
||||
//
|
||||
// Created by 孙慕 on 2018/8/28.
|
||||
// Copyright © 2018年 L. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface FUSquareButton : UIButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame interval:(float)interval;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// FUSquareButton.m
|
||||
// FULive
|
||||
//
|
||||
// Created by 孙慕 on 2018/8/28.
|
||||
// Copyright © 2018年 L. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FUSquareButton.h"
|
||||
|
||||
@interface FUSquareButton()
|
||||
|
||||
@property(nonatomic,assign) float interval;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUSquareButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame interval:(float)interval{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
_interval = interval;
|
||||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
_interval = 8;
|
||||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if (self = [super initWithCoder:aDecoder]) {
|
||||
_interval = 8;
|
||||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
CGRect frame = self.imageView.bounds;
|
||||
CGRect frame1 = self.titleLabel.frame;
|
||||
|
||||
self.imageView.frame = frame;
|
||||
CGPoint center = self.imageView.center;
|
||||
center.x = self.frame.size.width * 0.5;
|
||||
self.imageView.center = center;
|
||||
frame1.origin.x = 0;
|
||||
frame1.origin.y = CGRectGetMaxY(self.imageView.frame) + _interval;
|
||||
frame1.size.height = 14;
|
||||
frame1.size.width = self.bounds.size.width;;
|
||||
self.titleLabel.frame = frame1;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// FUTipHUD.h
|
||||
// FULiveDemo
|
||||
//
|
||||
// Created by 项林平 on 2021/4/12.
|
||||
// Copyright © 2021 FaceUnity. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FUTipHUDPosition) {
|
||||
FUTipHUDPositionTop,
|
||||
FUTipHUDPositionCenter
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUTipHUD : NSObject
|
||||
|
||||
/// 文字提示(默认3秒后自动消失)
|
||||
/// @param tipsString 文字
|
||||
+ (void)showTips:(NSString *)tipsString;
|
||||
|
||||
/// 文字提示
|
||||
/// @param tipsString 文字
|
||||
/// @param delay 自动消失时间,单位: 秒
|
||||
+ (void)showTips:(NSString *)tipsString dismissWithDelay:(NSTimeInterval)delay;
|
||||
|
||||
/// 文字提示
|
||||
/// @param tipsString 文字
|
||||
/// @param delay 自动消失时间,单位: 秒
|
||||
/// @param position 显示位置,默认为FUTipHUDPositionTop
|
||||
+ (void)showTips:(NSString *)tipsString dismissWithDelay:(NSTimeInterval)delay position:(FUTipHUDPosition)position;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// FUTipHUD.m
|
||||
// FULiveDemo
|
||||
//
|
||||
// Created by 项林平 on 2021/4/12.
|
||||
// Copyright © 2021 FaceUnity. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FUTipHUD.h"
|
||||
#import "FUInsetsLabel.h"
|
||||
|
||||
@implementation FUTipHUD
|
||||
|
||||
+ (void)showTips:(NSString *)tipsString {
|
||||
[self showTips:tipsString dismissWithDelay:3];
|
||||
}
|
||||
|
||||
+ (void)showTips:(NSString *)tipsString dismissWithDelay:(NSTimeInterval)delay {
|
||||
[self showTips:tipsString dismissWithDelay:delay position:FUTipHUDPositionTop];
|
||||
}
|
||||
|
||||
+ (void)showTips:(NSString *)tipsString dismissWithDelay:(NSTimeInterval)delay position:(FUTipHUDPosition)position {
|
||||
UIWindow *window = [UIApplication sharedApplication].delegate.window;
|
||||
// 避免重复生成label
|
||||
NSArray<UIView *> *views = window.subviews;
|
||||
[views enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([obj isMemberOfClass:[FUInsetsLabel class]]) {
|
||||
[obj removeFromSuperview];
|
||||
obj = nil;
|
||||
}
|
||||
}];
|
||||
|
||||
__block FUInsetsLabel *tipLabel = [[FUInsetsLabel alloc] initWithFrame:CGRectZero insets:UIEdgeInsetsMake(8, 20, 8, 20)];
|
||||
tipLabel.backgroundColor = [UIColor colorWithRed:5/255.0 green:15/255.0 blue:20/255.0 alpha:0.74];
|
||||
tipLabel.textColor = [UIColor whiteColor];
|
||||
tipLabel.font = [UIFont systemFontOfSize:13];
|
||||
tipLabel.numberOfLines = 0;
|
||||
tipLabel.layer.masksToBounds = YES;
|
||||
tipLabel.layer.cornerRadius = 4;
|
||||
tipLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
tipLabel.text = tipsString;
|
||||
[window addSubview:tipLabel];
|
||||
|
||||
if (position == FUTipHUDPositionTop) {
|
||||
CGFloat topConstant = 0;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
topConstant = window.safeAreaInsets.top;
|
||||
}
|
||||
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:tipLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:window attribute:NSLayoutAttributeTop multiplier:1 constant:84 + topConstant];
|
||||
[window addConstraint:topConstraint];
|
||||
} else {
|
||||
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:tipLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:window attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
|
||||
[window addConstraint:centerYConstraint];
|
||||
}
|
||||
CGFloat windowWidth = CGRectGetWidth(window.bounds);
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:tipLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:windowWidth - 40];
|
||||
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:tipLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:window attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
|
||||
[window addConstraint:centerXConstraint];
|
||||
[tipLabel addConstraint:widthConstraint];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
tipLabel.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
[tipLabel removeFromSuperview];
|
||||
tipLabel = nil;
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "slider_dot.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "slider_dot@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "slider_dot@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 191 B |
Binary file not shown.
|
After Width: | Height: | Size: 485 B |
Binary file not shown.
|
After Width: | Height: | Size: 1009 B |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "slider_tip_background.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "slider_tip_background@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "slider_tip_background@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 783 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Reference in New Issue
Block a user