美文网首页iOS小白聚集地
简易下拉控件,完全自定义

简易下拉控件,完全自定义

作者: 今晚月色 | 来源:发表于2018-08-21 08:49 被阅读281次
宝儿姐镇楼

最近自由时间有点多,就开始撸控件。这个是我们常用的一个下拉分类控件,看了很多别人写的,都是用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

如果有什么不好如果建议的地方,请批评指正!

相关文章

网友评论

本文标题:简易下拉控件,完全自定义

本文链接:https://www.haomeiwen.com/subject/ypfriftx.html