当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS 三级下拉菜单功能实现

iOS 三级下拉菜单功能实现

2019年07月24日  | 移动技术网IT编程  | 我要评论

熊猫宝宝集体出街,hsupdate.exe,搞笑二人转唐僧泡妞

前言

app 常用控件 -- 多级下拉菜单, 如团购类, 房屋类, 对数据进行筛选. 有一级, 二级, 三级, 再多就不会以这种样式,呈现给用户了. 作者就简单聊一下 多级下拉菜单


一 目标

  1. 默认显示一个 tableview, 点击数据后, 添加第二个tableview, 并实现大小变化
  2. 第二次打开下拉菜单. 保存上次选中数据

二 菜单控件dropmenuview

.h文件

#import <uikit/uikit.h>

@class dropmenuview;
@protocol dropmenuviewdelegate <nsobject>

-(void)dropmenuview:(dropmenuview *)view didselectname:(nsstring *)str;

@end

@interface dropmenuview : uiview

@property (nonatomic, weak) id<dropmenuviewdelegate> delegate;
/** 箭头变化 */
@property (nonatomic, strong) uiview *arrowview;

/**
 控件设置
 @param view 提供控件 位置信息
 @param tablenum 显示tableview数量
 @param arr 使用数据
 */
-(void)creatdropview:(uiview *)view withshowtablenum:(nsinteger)tablenum withdata:(nsarray *)arr;

/** 视图消失 */
- (void)dismiss;

@end

.m文件

#import "dropmenuview.h"

#define kwidth [uiscreen mainscreen].bounds.size.width
#define kheight [uiscreen mainscreen].bounds.size.height

@interface dropmenuview ()<uitableviewdelegate, uitableviewdatasource>
{
@private
  /** 保存 选择的数据(行数) */
  nsinteger selects[3];
}

@property (nonatomic, assign) bool show;  // 按钮点击后 视图显示/隐藏
@property (nonatomic, assign) cgfloat rowheightnum; // 设置 rom 高度

/* 底层取消按钮 */
@property (nonatomic, strong) uibutton *cancelbutton;
/** 表视图数组 */
@property (nonatomic, strong) nsarray *tableviewarr;
/** 表视图的 底部视图 */
@property (nonatomic, strong) uiview *tableviewunderview;
/** 显示 tableview 数量 */
@property (nonatomic, assign) nsinteger tablecount;
/** 数据 */
@property (nonatomic, strong) nsarray *dataarr;

@end


@implementation dropmenuview


- (instancetype)init
{
  self = [super init];
  if (self) {

    /** 数据初始化 */
    self.dataarr = [nsarray array];

    /** 保存 初始值为-1 */
    for (int i = 0; i < 3; i++) {
      selects[i] = -1;
    }

    /* 底层取消按钮 */
    self.cancelbutton = [uibutton buttonwithtype:uibuttontypecustom];
    self.cancelbutton.backgroundcolor = [uicolor colorwithwhite:0.0 alpha:0.3];
    [self.cancelbutton addtarget:self action:@selector(clickcancelbutton:) forcontrolevents:uicontroleventtouchupinside];
    [self addsubview:self.cancelbutton];

    /** 表视图的 底部视图初始化 */
    self.tableviewunderview = [[uiview alloc] init];
    self.tableviewunderview.backgroundcolor = [uicolor colorwithred:0.74 green:0.73 blue:0.76 alpha:1.000];
    [self.cancelbutton addsubview:self.tableviewunderview];

    /** 默认设置为no, row高度为40 */
    self.show = no;
    self.rowheightnum = 40.0f;

  }
  return self;
}


-(void)creatdropview:(uiview *)view withshowtablenum:(nsinteger)tablenum withdata:(nsarray *)arr{

  if (!self.show) {

    self.show = !self.show;

    // 显示 tableview数量
    self.tablecount = tablenum;

    // 数据
    self.dataarr = arr;
    for (uitableview *tableview in self.tableviewarr) {
      [tableview reloaddata];
    }

    // 初始位置 设置
    cgfloat x = 0.f;
    cgfloat y = view.frame.origin.y + view.frame.size.height;
    cgfloat w = kwidth;
    cgfloat h = kheight - y;

    self.frame = cgrectmake(x, y, w, h);
    self.cancelbutton.frame = cgrectmake(0, 0, self.frame.size.width, self.frame.size.height);
    self.tableviewunderview.frame = cgrectmake(0, 0, self.frame.size.width, self.rowheightnum * 7);

    if (!self.superview) {

      [[[uiapplication sharedapplication] keywindow] addsubview:self];
      self.alpha = 0.0f;
      [uiview animatewithduration:0.2f animations:^{
        self.alpha = 1.0f;
      }];


      [self loadselects];
      [self adjusttableviews];
    }

  }else{
    /** 什么也不选择时候, 再次点击按钮 消失视图 */
    [self dismiss];
  }
}


#pragma mark - 加载选中的tableview
-(void)loadselects{

  [self.tableviewarr enumerateobjectsusingblock:^(uitableview *tableview, nsuinteger idx, bool * _nonnull stop) {

    // 刷新tableview数据
    [tableview reloaddata];

    // 选中tableview某一行
    [tableview selectrowatindexpath:[nsindexpath indexpathforrow:selects[idx] insection:0] animated:no scrollposition:uitableviewscrollpositionnone];

    // 加 !idx 是因为 循环第一次 idx == 0 方法不执行, 所以需要循环一次 加载一个tableview.
    if((selects[idx] != -1 && !tableview.superview) || !idx) {

      [self.tableviewunderview addsubview:tableview];

      [uiview animatewithduration:0.2 animations:^{
        if (self.arrowview) {
          self.arrowview.transform = cgaffinetransformmakerotation(m_pi);
        }
      }];
    }
  }];

}

#pragma mark - 重置tableview的 位置
-(void)adjusttableviews{

  // 显示的 tableview 数量
  int addtablecount = 0;
  for (uitableview *tableview in self.tableviewarr) {

    if (tableview.superview) {
      addtablecount++;
    }
  }

  for (int i = 0; i < addtablecount; i++) {

    uitableview *tableview = self.tableviewarr[i];
    cgrect adjustframe = tableview.frame;

    adjustframe.size.width = kwidth / addtablecount ;
    adjustframe.origin.x = adjustframe.size.width * i + 0.5 * i;
    adjustframe.size.height = self.tableviewunderview.frame.size.height ;

    tableview.frame = adjustframe;
  }

}


#pragma mark - tableview协议

/** 行数 */
-(nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section{

  nsinteger __block count;
  [self.tableviewarr enumerateobjectsusingblock:^(id _nonnull obj, nsuinteger idx, bool * _nonnull stop) {

    if (obj == tableview) {

      nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row ; 
      nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row ;

      count = [self countforchoosetable:idx firsttableselectrow:firstselectrow withsecondtableselectrow:secondselectrow];
    }
  }];

  return count;
}

// 可以将 方法提出来, 如果有需要 可以设置为协议实现封装, 作者仅提取一个, 其他均在 tableview自身协议中写
-(nsinteger)countforchoosetable:(nsinteger)idx firsttableselectrow:(nsinteger)firstselectrow withsecondtableselectrow:(nsinteger)secondselectrow{

  if (idx == 0) {

    return self.dataarr.count;

  }else if (idx == 1){

    if (firstselectrow == -1) {

      return 0;

    }else{

      if (self.tablecount == 2) {

        return [self.dataarr[firstselectrow][@"subcategories"] count];

      }else{

        return [self.dataarr[firstselectrow][@"sub"] count];
      }

    }

  }else{

    if (secondselectrow == -1) {

      return 0;
    }else{

      return [self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"] count];

    }

  }
}






/** 自定义cell */
-(uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath{

  uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"dropcell"];
  cell.textlabel.font = [uifont systemfontofsize:14];


  if (self.tablecount == 1) {

    cell.textlabel.text = self.dataarr[indexpath.row][@"label"];

  }else if (self.tablecount == 2){

    nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row;

    if (tableview == self.tableviewarr[0]) {

      cell.textlabel.text = self.dataarr[indexpath.row][@"name"];

    }else if (tableview == self.tableviewarr[1]){

      cell.textlabel.text = self.dataarr[firstselectrow][@"subcategories"][indexpath.row];
    }

  }else if (self.tablecount == 3){

     nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row;
     nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row;

    if (tableview == self.tableviewarr[0]) {

      cell.textlabel.text = self.dataarr[indexpath.row][@"name"];

    }else if (tableview == self.tableviewarr[1]){

      cell.textlabel.text = self.dataarr[firstselectrow][@"sub"][indexpath.row][@"name"];

    }else if (tableview == self.tableviewarr[2]){


      cell.textlabel.text = self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"][indexpath.row];
    }
  }

  return cell;
}


/** 点击 */
-(void)tableview:(uitableview *)tableview didselectrowatindexpath:(nsindexpath *)indexpath{


  uitableview *secondtableview = self.tableviewarr[1];
  uitableview *thirdtableview = self.tableviewarr[2];

  if (self.tablecount == 1) {

    [self saveselects];
    [self dismiss];
    [_delegate dropmenuview:self didselectname:self.dataarr[indexpath.row][@"label"]];

  }else if (self.tablecount == 2){

    if (tableview == self.tableviewarr[0]) {

      if (!secondtableview.superview) {
        [self.tableviewunderview addsubview:secondtableview];
      }
      [secondtableview reloaddata];
      [self adjusttableviews];

    }else if (tableview == self.tableviewarr[1]){

      [self saveselects];
      [self dismiss];

       nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row;

       [_delegate dropmenuview:self didselectname:self.dataarr[firstselectrow][@"subcategories"][indexpath.row]];
    }

  }else if (self.tablecount == 3){

    nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row;
    nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row;

    if (tableview == self.tableviewarr[0]) {


      if (!secondtableview.superview) {
        [self.tableviewunderview addsubview:secondtableview];
      }
      [self adjusttableviews];
      [secondtableview reloaddata];

    }else if (tableview == self.tableviewarr[1]){


      if (!thirdtableview.superview) {
        [self.tableviewunderview addsubview:thirdtableview];
      }
      [self adjusttableviews];
      [thirdtableview reloaddata];

    }else if (tableview == self.tableviewarr[2]){

      [self saveselects];
      [self dismiss];
      [_delegate dropmenuview:self didselectname:self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"][indexpath.row]];

    }
  }

}


#pragma mark - 记录 选择状态
-(void)saveselects{

  [self.tableviewarr enumerateobjectsusingblock:^(uitableview *tableview, nsuinteger idx, bool * _nonnull stop) {

    selects[idx] = tableview.superview ? tableview.indexpathforselectedrow.row : -1;
  }];
}



#pragma mark - 视图消失
- (void)dismiss{

  if(self.superview) {

    self.show = !self.show;

    [self endediting:yes];

    [uiview animatewithduration:.25f animations:^{
      self.alpha = .0f;
    } completion:^(bool finished) {

      [self.tableviewunderview.subviews enumerateobjectsusingblock:^(uiview *obj, nsuinteger idx, bool *stop) {
        [obj removefromsuperview];
      }];

      [self removefromsuperview];
      [uiview animatewithduration:0.2 animations:^{
        if (self.arrowview) {
          self.arrowview.transform = cgaffinetransformmakerotation(0);
        }
      }];
    }];

  }
}

/** 底部按钮, 视图消失 */
-(void)clickcancelbutton:(uibutton *)button{

  [self dismiss];
}


/** 懒加载 */
-(nsarray *)tableviewarr{

  if (_tableviewarr == nil) {

    _tableviewarr = @[[[uitableview alloc] init], [[uitableview alloc] init], [[uitableview alloc] init]];

    for (uitableview *tableview in _tableviewarr) {

      [tableview registerclass:[uitableviewcell class] forcellreuseidentifier:@"dropcell"];
      tableview.delegate = self;
      tableview.datasource = self;
      tableview.frame = cgrectmake(0, 0, 0, 0);
      tableview.backgroundcolor = [uicolor whitecolor];
      tableview.tablefooterview = [[uiview alloc] init];
      tableview.showsverticalscrollindicator = no;
      tableview.rowheight = self.rowheightnum;
    }
  }

  return _tableviewarr;
}

@end

三 调用控件menuscreeningview

.h文件

#import <uikit/uikit.h>

@interface menuscreeningview : uiview

#pragma mark - 筛选菜单消失
-(void)menuscreeningviewdismiss;

@end

.m文件

#import "menuscreeningview.h"
#import "dropmenuview.h"

#define kwidth [uiscreen mainscreen].bounds.size.width
#define kheight [uiscreen mainscreen].bounds.size.height

@interface menuscreeningview ()<dropmenuviewdelegate>

@property (nonatomic, strong) uibutton *onelinkagebutton;
@property (nonatomic, strong) uibutton *twolinkagebutton;
@property (nonatomic, strong) uibutton *threelinkagebutton;

@property (nonatomic, strong) dropmenuview *onelinkagedropmenu;
@property (nonatomic, strong) dropmenuview *twolinkagedropmenu;
@property (nonatomic, strong) dropmenuview *threelinkagedropmenu;

@property (nonatomic, strong) nsarray *addressarr;
@property (nonatomic, strong) nsarray *categoriesarr;
@property (nonatomic, strong) nsarray *sortsarr;


@end


@implementation menuscreeningview

- (instancetype)initwithframe:(cgrect)frame
{
  self = [super initwithframe:frame];
  if (self) {


    self.onelinkagebutton = [uibutton buttonwithtype:uibuttontypecustom];
    self.onelinkagebutton.frame = cgrectmake(0, 0, kwidth/3, 36);
    [self setupbutton:self.onelinkagebutton withtext:@"一级"];

    self.onelinkagedropmenu = [[dropmenuview alloc] init];
    self.onelinkagedropmenu.arrowview = self.onelinkagebutton.imageview;
    self.onelinkagedropmenu.delegate = self;



    self.twolinkagebutton = [uibutton buttonwithtype:uibuttontypecustom];
    self.twolinkagebutton.frame = cgrectmake(kwidth/3, 0, kwidth/3, 36);
    [self setupbutton:self.twolinkagebutton withtext:@"二级"];

    self.twolinkagedropmenu = [[dropmenuview alloc] init];
    self.twolinkagedropmenu.arrowview = self.twolinkagebutton.imageview;
    self.twolinkagedropmenu.delegate = self;



    self.threelinkagebutton = [uibutton buttonwithtype:uibuttontypecustom];
    self.threelinkagebutton.frame = cgrectmake(2 * kwidth/3, 0, kwidth/3, 36);
    [self setupbutton:self.threelinkagebutton withtext:@"三级"];

    self.threelinkagedropmenu = [[dropmenuview alloc] init];
    self.threelinkagedropmenu.arrowview = self.threelinkagebutton.imageview;
    self.threelinkagedropmenu.delegate = self;


    /** 最下面横线 */
    uiview *horizontalline = [[uiview alloc] initwithframe:cgrectmake(0, self.frame.size.height - 0.6, kwidth, 0.6)];
    horizontalline.backgroundcolor = [uicolor colorwithwhite:0.8 alpha:1.000];
    [self addsubview:horizontalline];


  }
  return self;
}



#pragma mark - 按钮点击推出菜单 (并且其他的菜单收起)
-(void)clickbutton:(uibutton *)button{


  if (button == self.onelinkagebutton) {

    [self.twolinkagedropmenu dismiss];
    [self.threelinkagedropmenu dismiss];

    [self.onelinkagedropmenu creatdropview:self withshowtablenum:1 withdata:self.sortsarr];

  }else if (button == self.twolinkagebutton){

    [self.onelinkagedropmenu dismiss];
    [self.threelinkagedropmenu dismiss];

    [self.twolinkagedropmenu creatdropview:self withshowtablenum:2 withdata:self.categoriesarr];

  }else if (button == self.threelinkagebutton){

    [self.onelinkagedropmenu dismiss];
    [self.twolinkagedropmenu dismiss];

    [self.threelinkagedropmenu creatdropview:self withshowtablenum:3 withdata:self.addressarr];
  }
}



#pragma mark - 筛选菜单消失
-(void)menuscreeningviewdismiss{

  [self.onelinkagedropmenu dismiss];
  [self.twolinkagedropmenu dismiss];
  [self.threelinkagedropmenu dismiss];
}


#pragma mark - 协议实现
-(void)dropmenuview:(dropmenuview *)view didselectname:(nsstring *)str{

  if (view == self.onelinkagedropmenu) {

    [self.onelinkagebutton settitle:str forstate:uicontrolstatenormal];
    [self buttonedgeinsets:self.onelinkagebutton];

  }else if (view == self.twolinkagedropmenu){

    [self.twolinkagebutton settitle:str forstate:uicontrolstatenormal];
    [self buttonedgeinsets:self.twolinkagebutton];

  }else if (view == self.threelinkagedropmenu){

    [self.threelinkagebutton settitle:str forstate:uicontrolstatenormal];
    [self buttonedgeinsets:self.threelinkagebutton];

  }
}





#pragma mark - 设置button
-(void)setupbutton:(uibutton *)button withtext:(nsstring *)str{

  [button addtarget:self action:@selector(clickbutton:) forcontrolevents:uicontroleventtouchupinside];
  [self addsubview:button];
  [button settitle:str forstate:uicontrolstatenormal];
  button.titlelabel.font = [uifont systemfontofsize:11];
  button.titlelabel.linebreakmode = nslinebreakbytruncatingtail;
  [button settitlecolor:[uicolor colorwithwhite:0.3 alpha:1.000] forstate:uicontrolstatenormal];
  [button setimage:[uiimage imagenamed:@"downarr"] forstate:uicontrolstatenormal];

  [self buttonedgeinsets:button];

  uiview *verticalline = [[uiview alloc]init];
  verticalline.backgroundcolor = [uicolor colorwithwhite:0.9 alpha:1.0];
  [button addsubview:verticalline];
  verticalline.frame = cgrectmake(button.frame.size.width - 0.5, 3, 0.5, 30);
}

-(void)buttonedgeinsets:(uibutton *)button{

  [button settitleedgeinsets:uiedgeinsetsmake(0, -button.imageview.bounds.size.width + 2, 0, button.imageview.bounds.size.width + 10)];
  [button setimageedgeinsets:uiedgeinsetsmake(0, button.titlelabel.bounds.size.width + 10, 0, -button.titlelabel.bounds.size.width + 2)];

}




#pragma mark - 懒加载
-(nsarray *)addressarr{

  if (_addressarr == nil) {

    nsdictionary *dic = [nsdictionary dictionarywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"address.plist" oftype:nil]];

    _addressarr = dic[@"address"];
  }

  return _addressarr;
}

-(nsarray *)categoriesarr{

  if (_categoriesarr == nil) {

    _categoriesarr = [nsarray arraywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"categories.plist" oftype:nil]];
  }

  return _categoriesarr;
}

-(nsarray *)sortsarr{

  if (_sortsarr == nil) {

    _sortsarr = [nsarray arraywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"sorts.plist" oftype:nil]];
  }

  return _sortsarr;
}

@end

四 调用

   menuscreeningview *menuscreening = [[menuscreeningview alloc] initwithframe:cgrectmake(0, 64, kwidth, 36)];
  [self.view addsubview:menuscreening];
  menuscreening.backgroundcolor = [uicolor whitecolor];

五 效果图


六 demo下载

因为数据源 无法上次上传[简书], 所以上传个demo, 细节方面, 可能有未注意地方,仅供参考.

传送门 :

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

  • ios uicollectionview实现横向滚动

    现在使用卡片效果的app很多,之前公司让实现一种卡片效果,就写了一篇关于实现卡片的文章。文章最后附有demo实现上我选择了使用uicollectionview ... [阅读全文]
  • iOS UICollectionView实现横向滑动

    本文实例为大家分享了ios uicollectionview实现横向滑动的具体代码,供大家参考,具体内容如下uicollectionview的横向滚动,目前我使... [阅读全文]
  • iOS13适配深色模式(Dark Mode)的实现

    iOS13适配深色模式(Dark Mode)的实现

    好像大概也许是一年前, mac os系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的终于, 随着iphone11等新手机的发售, ios 13系统... [阅读全文]
  • ios 使用xcode11 新建项目工程的步骤详解

    ios 使用xcode11 新建项目工程的步骤详解

    xcode11新建项目工程,新增了scenedelegate这个类,转而将原appdelegate负责的对ui生命周期的处理担子接了过来。故此可以理解为:ios... [阅读全文]
  • iOS实现转盘效果

    本文实例为大家分享了ios实现转盘效果的具体代码,供大家参考,具体内容如下demo下载地址: ios转盘效果功能:实现了常用的ios转盘效果,轮盘抽奖效果的实现... [阅读全文]
  • iOS开发实现转盘功能

    本文实例为大家分享了ios实现转盘功能的具体代码,供大家参考,具体内容如下今天给同学们讲解一下一个转盘选号的功能,直接上代码直接看viewcontroller#... [阅读全文]
  • iOS实现轮盘动态效果

    本文实例为大家分享了ios实现轮盘动态效果的具体代码,供大家参考,具体内容如下一个常用的绘图,主要用来打分之类的动画,效果如下。主要是ios的绘图和动画,本来想... [阅读全文]
  • iOS实现九宫格连线手势解锁

    本文实例为大家分享了ios实现九宫格连线手势解锁的具体代码,供大家参考,具体内容如下demo下载地址:效果图:核心代码://// clockview.m// 手... [阅读全文]
  • iOS实现卡片堆叠效果

    本文实例为大家分享了ios实现卡片堆叠效果的具体代码,供大家参考,具体内容如下如图,这就是最终效果。去年安卓5.0发布的时候,当我看到安卓全新的material... [阅读全文]
  • iOS利用余弦函数实现卡片浏览工具

    iOS利用余弦函数实现卡片浏览工具

    本文实例为大家分享了ios利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下一、实现效果通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间... [阅读全文]
验证码:
移动技术网