美文网首页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