最近自由时间有点多,就开始撸控件。这个是我们常用的一个下拉分类控件,看了很多别人写的,都是用TableView或者CollectionView整合在一起的感觉自定义性没有那么强,所以就尝试用把TableView和CollectionView替换成UIViewController。其他废话不多说了。
#import <UIKit/UIKit.h>
extern NSString * const WDchangeTitle;
@interface WDDropDownView : UIView
@property (nonatomic, strong) NSArray<NSString *> *titleArray;
@property (nonatomic, strong) NSArray<UIViewController *> *controllerArray;
@property (nonatomic, strong) NSArray *controllerHeightArray;
- (instancetype)initWithTitleArray:(NSArray<NSString *> *)titleArray
controllerArray:(NSArray<UIViewController *> *)controllerArray
controllerHeightArray:(NSArray *)controllerHeightArray;
@end
上面按钮显示样式,任君自由定制。
#import "WDDropDownView.h"
typedef enum : NSUInteger {
WDDropDownButtonStyle_RightImage,
WDDropDownButtonStyle_LeftImage,
} WDDropDownButtonStyle;
@interface WDDropDownButton : UIButton
- (void)wd_layoutButtonWithEdgeInsetsStyle:(WDDropDownButtonStyle)style
imageTitleSpace:(CGFloat)space;
@end
@implementation WDDropDownButton
- (void)wd_layoutButtonWithEdgeInsetsStyle:(WDDropDownButtonStyle)style
imageTitleSpace:(CGFloat)space {
CGFloat imageWith = self.imageView.frame.size.width;
CGFloat labelWidth = 0.0;
CGFloat labelHeight = 0.0;
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
labelWidth = self.titleLabel.intrinsicContentSize.width;
labelHeight = self.titleLabel.intrinsicContentSize.height;
} else {
labelWidth = self.titleLabel.frame.size.width;
labelHeight = self.titleLabel.frame.size.height;
}
UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero;
UIEdgeInsets labelEdgeInsets = UIEdgeInsetsZero;
switch (style) {
case WDDropDownButtonStyle_LeftImage:
{
imageEdgeInsets = UIEdgeInsetsMake(0, -space / 2.0, 0, space / 2.0);
labelEdgeInsets = UIEdgeInsetsMake(0, space / 2.0, 0, -space / 2.0);
}
break;
case WDDropDownButtonStyle_RightImage:
{
imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth + space / 2.0, 0, -labelWidth - space / 2.0);
labelEdgeInsets = UIEdgeInsetsMake(0, -imageWith - space / 2.0, 0, imageWith + space / 2.0);
}
break;
default:
break;
}
self.titleEdgeInsets = labelEdgeInsets;
self.imageEdgeInsets = imageEdgeInsets;
}
@end
#define WD_SCR_WIDTH UIScreen.mainScreen.bounds.size.width
#define WD_SCR_HEIGHT UIScreen.mainScreen.bounds.size.height
NSString * const WDchangeTitle = @"wd_changeTitle";
@interface WDDropDownView ()
@property (nonatomic, strong) UIView *headerButtonView;
@property (nonatomic, strong) UIButton *converButton;
@property (nonatomic, strong) UIView *bottomLine;
@property (nonatomic, strong) UIButton *lastSelectedButton;
@end
@implementation WDDropDownView
- (instancetype)initWithTitleArray:(NSArray<NSString *> *)titleArray controllerArray:(NSArray<UIViewController *> *)controllerArray controllerHeightArray:(NSArray *)controllerHeightArray {
self = [super init];
if(self){
self.lastSelectedButton = UIButton.new;
self.titleArray = titleArray;
self.controllerArray = controllerArray;
self.controllerHeightArray = controllerHeightArray;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealWithNotification:) name:WDchangeTitle object:nil];
}
return self;
}
- (instancetype)init{
self = [super init];
if(self){
self.lastSelectedButton = UIButton.new;
}
return self;
}
- (void)layoutSubviews {
[self addSubview:self.headerButtonView];
[self addSubview:self.bottomLine];
[self insetButton];
}
- (void)dealWithNotification:(NSNotification *)notification {
if (![self.controllerArray containsObject:notification.object]) {
return;
}
[self converButtonDismiss];
NSArray *allValues = notification.userInfo.allValues;
[self.lastSelectedButton setTitle:allValues.firstObject forState:UIControlStateNormal];
}
- (void)setControllerHeightArray:(NSArray *)controllerHeightArray{
_controllerHeightArray = controllerHeightArray;
}
- (void)setControllerArray:(NSArray<UIViewController *> *)controllerArray {
_controllerArray = controllerArray;
}
- (void)setTitleArray:(NSArray<NSString *> *)titleArray {
_titleArray = titleArray;
}
- (void)insetButton{
if (self.titleArray.count != self.controllerArray.count) {
@throw [NSException exceptionWithName:NSStringFromClass(self.class) reason:@"标题和控制器数量不对应" userInfo:nil];
}
CGFloat Width = WD_SCR_WIDTH/self.titleArray.count;
for (int i=0; i<self.titleArray.count; i++) {
WDDropDownButton *b = [[WDDropDownButton alloc] init];
[b setTitle:self.titleArray[i] forState:UIControlStateNormal];
[b setImage:[UIImage imageNamed:@"arrow_down"] forState:UIControlStateNormal];
[b setImage:[UIImage imageNamed:@"arrow_up"] forState:UIControlStateSelected];
[b setTitleColor:UIColor.lightGrayColor forState:UIControlStateNormal];
[b setTitleColor:UIColor.orangeColor forState:UIControlStateSelected];
b.frame = CGRectMake(Width * i, 0, Width, self.frame.size.height);
b.tag = i + 1;
[b wd_layoutButtonWithEdgeInsetsStyle:WDDropDownButtonStyle_RightImage imageTitleSpace:10];
[b addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.headerButtonView addSubview:b];
}
}
- (void)buttonAction:(UIButton *)sender {
sender.selected = !sender.selected;
if (self.lastSelectedButton && self.lastSelectedButton != sender) {
self.lastSelectedButton.selected = NO;
}
self.lastSelectedButton = sender;
if (self.lastSelectedButton.selected) {
[self converButtonShowWithTag:sender.tag-1];
} else {
[self converButtonDismiss];
}
}
- (void)converButtonShowWithTag:(NSInteger)tag{
for(UIView *view in self.converButton.subviews){
[view removeFromSuperview];
}
UIViewController *currentVC = self.controllerArray[tag];
self.converButton.alpha = 0;
self.converButton.frame = CGRectMake(0, self.headerButtonView.frame.size.height+self.frame.origin.y+1, WD_SCR_WIDTH, 0);
currentVC.view.frame = CGRectMake(0, 0, WD_SCR_WIDTH, 0);
[UIView animateWithDuration:0.3f animations:^{
self.converButton.alpha = 1;
self.converButton.frame = CGRectMake(0, self.headerButtonView.frame.size.height+self.frame.origin.y+1, WD_SCR_WIDTH, WD_SCR_HEIGHT-self.headerButtonView.frame.size.height);
currentVC.view.frame = CGRectMake(0, 0, WD_SCR_WIDTH, [self.controllerHeightArray[tag] floatValue]);
} completion:^(BOOL finished) {
[self.superview addSubview:self.converButton];
[self.converButton addSubview:currentVC.view];
}];
}
- (void)converButtonDismiss {
self.lastSelectedButton.selected = false;
[UIView animateWithDuration:0.3f animations:^{
self.converButton.alpha = 0;
self.converButton.frame = CGRectMake(0, self.headerButtonView.frame.size.height+self.frame.origin.y+1, WD_SCR_WIDTH, 0);
} completion:^(BOOL finished) {
[self.converButton removeFromSuperview];
}];
}
- (UIView *)headerButtonView {
if (!_headerButtonView) {
_headerButtonView = ({
UIView *v = [[UIView alloc] init];
v.backgroundColor = UIColor.whiteColor;
v.frame = CGRectMake(0, 0, WD_SCR_WIDTH, self.frame.size.height);
v;
});
}
return _headerButtonView;
}
- (UIButton *)converButton {
if (!_converButton) {
_converButton = ({
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
b.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3];
[b addTarget:self action:@selector(converButtonDismiss) forControlEvents:UIControlEventTouchUpInside];
b;
});
}
return _converButton;
}
- (UIView *)bottomLine {
if (!_bottomLine) {
_bottomLine = ({
UIView *v = [[UIView alloc] init];
v.backgroundColor = UIColor.lightGrayColor;
v.frame = CGRectMake(0, self.frame.size.height-0.5, WD_SCR_WIDTH, 0.5);
v;
});
}
return _bottomLine;
}
@end
使用方式有两种
一、直接使用init初始化然后再设置属性
WDDropDownView *test = [[WDDropDownView alloc] init];
test.titleArray = @[@"区域", @"分类", @"排序"];
test.controllerArray = @[vc1,vc2, vc3];
test.controllerHeightArray = @[@(100), @200, @300]];
[self.view addSubview:test];
二、init直接设置属性
WDDropDownView *test = [[WDDropDownView alloc] initWithTitleArray: @[@"区域",@"分类",@"排序"]
controllerArray: @[vc1,vc1,vc1]
controllerHeightArray: @[@(100),@200,@300]];
[self.view addSubview:test];
布局可以使用Frame和自动布局
test.frame = CGRectMake(0, UIApplication.sharedApplication.statusBarFrame.size.height+44, self.view.frame.size.width, 45);
[test mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.mas_equalTo(self.view);
make.top.mas_equalTo(self.view).inset(UIApplication.sharedApplication.statusBarFrame.size.height+44);
make.height.mas_equalTo(45);
}];
点击controller中的数据显示在按钮的标题中,这因为跨界面显示,我暂时没有想到好的办法,所以使用强大的通知传值。
// controller中发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:WDchangeTitle object:self userInfo:@{@"title":string}];
效果图
效果图
补充Swift版本
import UIKit
enum WDDropDownButtonStyle {
case WDDropDownButtonStyle_RightImage
case WDDropDownButtonStyle_LeftImage
}
extension UIButton {
func layoutButtonEdgeInsets(style:WDDropDownButtonStyle ,imageTitleSpeace:CGFloat) -> Void {
let imageWidth:CGFloat = (self.imageView?.frame.size.width)!
var labelWidth:CGFloat = 0.0
var labelHeight:CGFloat = 0.0
if #available(iOS 8.0, *){
labelWidth = (titleLabel?.intrinsicContentSize.width)!
labelHeight = (titleLabel?.intrinsicContentSize.height)!
} else{
labelWidth = (titleLabel?.frame.size.width)!
labelHeight = (titleLabel?.frame.size.height)!
}
var temp_imageEdgeInserts:UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
var temp_titleEdgeInserts:UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
switch style {
case .WDDropDownButtonStyle_LeftImage:
temp_imageEdgeInserts = UIEdgeInsetsMake(0, -imageTitleSpeace/2.0, 0, imageTitleSpeace/2.0)
temp_titleEdgeInserts = UIEdgeInsetsMake(0, imageTitleSpeace/2.0, 0, -imageTitleSpeace/2.0)
break
case .WDDropDownButtonStyle_RightImage:
temp_imageEdgeInserts = UIEdgeInsetsMake(0, labelWidth+imageTitleSpeace/2.0, 0,-labelWidth-imageTitleSpeace/2.0)
temp_titleEdgeInserts = UIEdgeInsetsMake(0, -imageWidth-imageTitleSpeace/2.0, 0, imageWidth+imageTitleSpeace/2.0)
break
}
titleEdgeInsets = temp_titleEdgeInserts
imageEdgeInsets = temp_imageEdgeInserts
}
}
class WDDropDown_Swift: UIView {
/// 通知名称
public let WDchangeTitle = "wd_changeTitle"
/// 标题
private var _titleArray: Array<String>?
public var titleArray: Array<String>? {
get {
return _titleArray
}
set {
_titleArray = titleArray
}
}
/// 控制器
private var _controllerArray: Array<UIViewController>?
public var controllerArray: Array<UIViewController>? {
get {
return _controllerArray
}
set {
_controllerArray = controllerArray
}
}
/// 控制器高度
private var _controllerHeightArray: Array<CGFloat>?
public var controllerHeightArray: Array<CGFloat>? {
get {
return _controllerHeightArray
}
set {
_controllerHeightArray = controllerHeightArray
}
}
/// 按钮容器视图
private lazy var headerButtonView:UIView = {
let v = UIView.init()
v.backgroundColor = UIColor.white
v.frame = CGRect(x: 0, y: 0, width: WD_SCR_WIDTH, height: self.frame.width)
return v
}()
/// 控制容器视图
private lazy var convertButton:UIButton = {
let b = UIButton.init()
b.backgroundColor = UIColor.init(white: 0, alpha: 0.3)
b.addTarget(self, action: #selector(convertButtonDismiss), for: .touchUpInside)
return b
}()
/// 底部分割线
private lazy var bottomLine:UIView = {
let v = UIView.init()
v.backgroundColor = UIColor.lightGray
v.frame = CGRect(x: 0, y: self.frame.height-0.5, width: WD_SCR_WIDTH, height: 0.5)
return v
}()
/// 最后选中按钮
private var lastSelecedButton:UIButton?
private let WD_SCR_WIDTH = UIScreen.main.bounds.size.width
private let WD_SCR_HEIGHT = UIScreen.main.bounds.size.height
private var WD_HeaderY:CGFloat?
/// 初始化
///
/// - Parameters:
/// - titleArray: 标题数组
/// - controllerArray: 控制器数组
/// - controllerHeightArray: 控制器显示高度数据
convenience init(titleArray:Array<String>, controllerArray: Array<UIViewController>, controllerHeightArray: Array<CGFloat>) {
self.init()
self._titleArray = titleArray
self._controllerArray = controllerArray
self._controllerHeightArray = controllerHeightArray
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 设置视图
override func layoutSubviews() {
WD_HeaderY = self.frame.height+self.frame.origin.y
self.addSubview(headerButtonView)
self.addSubview(bottomLine)
self.lastSelecedButton = UIButton.init()
insetButton()
NotificationCenter.default.addObserver(self, selector: #selector(dealWithNotification(notification:)), name: NSNotification.Name(rawValue: WDchangeTitle), object: nil)
}
@objc private func dealWithNotification(notification:Notification) {
if (_controllerArray?.contains(notification.object as! UIViewController))! {
convertButtonDismiss()
let allValues = notification.userInfo?.values
lastSelecedButton?.setTitle(allValues?.first as? String, for: .normal)
} else {
return
}
}
@objc
/// 绘制页面
private func insetButton() {
if _titleArray?.count != _controllerArray?.count {
fatalError("标题和控制器数量不对应")
}
let Width = WD_SCR_WIDTH/CGFloat((_titleArray?.count)!)
for (i,_) in (_titleArray?.enumerated())! {
let b:UIButton = UIButton.init()
b.frame = CGRect(x: Width*CGFloat(i), y: 0, width: Width, height: self.frame.height)
b.setTitle(_titleArray?[i], for: .normal)
b.setImage(UIImage.init(named: "arrow_down"), for: .normal)
b.setImage(UIImage.init(named: "arrow_up"), for: .selected)
b.setTitleColor(UIColor.lightGray, for: .normal)
b.setTitleColor(UIColor.orange, for: .selected)
b.tag = i+1
b.layoutButtonEdgeInsets(style: .WDDropDownButtonStyle_RightImage, imageTitleSpeace: 10)
b.addTarget(self, action: #selector(buttonAction(sender:)), for: .touchUpInside)
headerButtonView.addSubview(b)
}
}
/// 标题点击事件
///
/// - Parameter sender: button
@objc private func buttonAction(sender:UIButton) {
sender.isSelected = !sender.isSelected
if (lastSelecedButton != nil) && lastSelecedButton != sender {
lastSelecedButton?.isSelected = false
}
lastSelecedButton = sender
if (lastSelecedButton?.isSelected)! {
convertButtonShow(buttonTag: sender.tag-1)
} else {
convertButtonDismiss()
}
}
/// 控制器消失
@objc private func convertButtonDismiss() {
lastSelecedButton?.isSelected = false
UIView.animate(withDuration: 0.3, animations: {
self.convertButton.alpha = 0
self.convertButton.frame = CGRect(x: 0, y: self.WD_HeaderY!, width: self.WD_SCR_WIDTH, height: 0)
}) { (bool) in
self.convertButton.removeFromSuperview()
}
}
/// 控制器显示
///
/// - Parameter buttonTag: 显示对应的控制器
private func convertButtonShow(buttonTag:Int) {
for view:UIView in self.convertButton.subviews {
view.removeFromSuperview()
}
let currentVC:UIViewController = _controllerArray![buttonTag]
convertButton.alpha = 0
convertButton.frame = CGRect(x: 0, y: self.WD_HeaderY!+1, width: WD_SCR_WIDTH, height: 0)
currentVC.view.frame = CGRect(x: 0, y: 0, width: WD_SCR_WIDTH, height: 0)
currentVC.view.clipsToBounds = true
let controllerHeight:CGFloat = self._controllerHeightArray![buttonTag]
UIView.animate(withDuration: 0.3, animations: {
self.convertButton.alpha = 1
self.convertButton.frame = CGRect(x: 0, y: self.WD_HeaderY!+1, width: self.WD_SCR_WIDTH, height: self.WD_SCR_HEIGHT-self.WD_HeaderY!)
currentVC.view.frame = CGRect(x: 0, y: 0, width: self.WD_SCR_WIDTH, height: controllerHeight)
}) { (bool) in
self.superview?.addSubview(self.convertButton)
self.convertButton.addSubview(currentVC.view)
}
}
}
Demo地址
https://github.com/wudan-ios/WDDropDown.git
如果有什么不好如果建议的地方,请批评指正!
网友评论