欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS 三级下拉菜单功能实现

程序员文章站 2023-12-20 16:50:52
前言 app 常用控件 -- 多级下拉菜单, 如团购类, 房屋类, 对数据进行筛选. 有一级, 二级, 三级, 再多就不会以这种样式,呈现给用户了. 作者就简单聊一下 多...

前言

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

iOS 三级下拉菜单功能实现

一 目标

  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];

五 效果图

iOS 三级下拉菜单功能实现

六 demo下载

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

传送门 : linkagemenu_jb51.rar

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

上一篇:

下一篇: