UI学习(三)

发布于:2025-06-11 ⋅ 阅读:(27) ⋅ 点赞:(0)

多界面传值

多界面传值是在不同视图控制器之间传递数据的过程。主要实现方法为定义一个代理(delegate)对象,通过代理对象实现协议函数使原来访问不到第一个对象的第二个对象可以访问第一个对象。

代码实现:

定义两个视图控制器,在第二个视图控制器中定义视图控制器一为代理对象来实现代理协议,使在视图控制器二中可以改变视图控制一的颜色,再让视图控制器一遵守视图控制器二中的代理协议,并在实现方法部分实现该代理协议。

视图控制器二:

//SecondViewController.h
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
//定义视图控制器二的代理协议
@protocol SecondDelegate <NSObject>

-(void)changeColor:(UIColor*)color;

@end
@interface SecondViewController : UIViewController
@property(assign, nonatomic) NSInteger tag;
//定义代理属性执行代理函数
//代理对象一定要实现协议
@property(nonatomic, weak) id<SecondDelegate> delegate;
@end
//SecondViewController.m
#import "SecondViewController.h"

@interface SecondViewController ()

@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    UIBarButtonItem *btnChange = [[UIBarButtonItem alloc] initWithTitle:@"改变颜色" style:UIBarButtonItemStyleDone target:self action:@selector(pressBtn)];
    self.navigationItem.rightBarButtonItem = btnChange;
}
-(void)pressBtn {
    [_delegate changeColor:[UIColor blueColor]];
}

视图控制器一:

//FirstViewController.h
//申明要遵守的协议
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
@interface FirstViewController : UIViewController<SecondDelegate>
@end
//FirstViewController.m
#import "FirstViewController.h"
@interface FirstViewController ()

@end

@implementation FirstViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"根视图";
    self.view.backgroundColor = [UIColor yellowColor];
    self.navigationController.navigationBar.translucent = NO;
    UINavigationBarAppearance *app = [[UINavigationBarAppearance alloc] init];
    app.backgroundColor = [UIColor greenColor];
    app.shadowImage = [[UIImage alloc] init];
    app.shadowColor = nil;
    self.navigationController.navigationBar.standardAppearance = app;
    self.navigationController.navigationBar.scrollEdgeAppearance = app;
}
//点击视图空白处时推出视图控制器二
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    SecondViewController *vc2 = [[SecondViewController alloc] init];
    vc2.view.backgroundColor = [UIColor grayColor];
    //将当前控制器作为代理对象
    vc2.delegate = self;
    [self.navigationController pushViewController:vc2 animated:YES];
}
-(void)changeColor:(UIColor *)color {
    self.view.backgroundColor = color;
}

运行结果:

在这里插入图片描述

UITableView

UITableView基础

UITableView是指数据视图,如表格。

在创建UITableView时,必须实现协议中的几个函数:

  • 获取组数:numberOfSectionsInTableView
  • 获取每组元素个数:tableView
  • 创建单元格:cellForRowAtIndexPath
  • 创建单元格对象函数:numberOfSectionsInTableView
  • 设置数据代理对象:dataSource:
  • 设置普通代理对象:delegate
  • 获取行数:numberOfRowsInSection

演示一下代码:

//ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
    UITableView *_tableView;
}

@end
//ViewController.m
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建数据视图
    //参数1:数据视图的位置
    //参数2:数据视图的风格
    _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];//分组风格
    _tableView.delegate = self;//代理对象
    _tableView.dataSource = self;//代理源对象
    [self.view addSubview:_tableView];
}

//获取每组元素个数(行数)
//参数1:数据视图对象
//参数2:需要的行数
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 5;
}

//设置数据视图组数
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 3;
}

//创建单元格对象函数
//参数1:函数的对象
//参数2:单元格索引(即行数组数)
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *cellStr = @"cell";
    UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:cellStr];
    if (cell == nil) {
        //创建单元格对象
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStr];
    }
    NSString *str = [NSString stringWithFormat:@"第%ld组,第%ld行", indexPath.section, indexPath.row];
    cell.textLabel.text = str;
    return cell;
}
@end

运行效果:

在这里插入图片描述

其中,区分一下delegate 和 dataSource:

属性 协议 主要职责
delegate UITableViewDelegate 处理视图交互和外观 (点击、滚动、行高、单元格显示/隐藏等)
dataSource UITableViewDataSource 提供数据内容和结构 (行数、单元格内容、编辑操作等)

dataSource的两个方法(返回行数和单元格)必须实现,delegate的方法可以选择性实现。通俗的说,对于一个餐厅,dataSource可以类比为准备食物的厨师,delegate可以类比为处理顾客需求的服务员。

UITableView协议

如果我们想修改这个单元格的样式,我们可以通过以下相应的函数:

  • 获取单元格高度:heightForRowAtIndexPath
  • 获取数据视图头部高度:heightForHeaderInSection
  • 获取数据视图尾部高度:heightForFooterInSection
  • 获取数据视图尾部标题:titleForFooterInSection
  • 获取数据视图头部标题:titleForHeaderInSection

演示下代码:

//ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
    UITableView *_tableView;
    NSMutableArray *_arrayData;//数据源
}


@end
//ViewController.m
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 40, 394, 825) style:UITableViewStyleGrouped];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    [self.view addSubview:_tableView];
    _arrayData = [[NSMutableArray alloc] init];
    //创建二维数组的结构源
    for (int i = 'A'; i < 'Z'; i++) {
        NSMutableArray *arraySmall = [[NSMutableArray alloc] init];
        for (int j = 1; j <= 5; j++) {
            NSString *str = [NSString stringWithFormat:@"%c%d", i, j];
            [arraySmall addObject:str];
        }
        [_arrayData addObject:arraySmall];
    }
}

//获取组数
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return _arrayData.count;
}

//获取每组元素个数
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger numRow = [[_arrayData objectAtIndex:section] count];//获取一维数组的个数,即每组元素个数
    return numRow;
}

//获取单元格
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //定义一个重用标识符
    NSString *str = @"cell";
    //创建一个类型为'cell'的UITableViewCell
    //dequeueReusableCellWithIdentifier:在重用队列里查找是否有可复用的cell(即已创建但不在屏幕上)
    UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:str];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:str];
    }
    cell.textLabel.text = _arrayData[indexPath.section][indexPath.row];
    return cell;
}

//协议函数:
//获取单元格高度
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 100;
}

//获取显示在每组头部的标题
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return @"头部标题";
}

//获取尾部标题
-(NSString*)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    return @"尾部标题";
}

//获取头部高度
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 40;
}

//获取尾部高度
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 20;
}

@end

运行效果:

在这里插入图片描述

UITableView高级协议与单元格

在上面的基础,我们再认识几个更高级的协议函数:

  • 提交编辑函数:commitEditingStyle

  • 开启关闭编辑单元格:canEditRowAtIndexPath

  • 编辑单元格风格设定:editingStyleForRowAtIndexPath

  • 选中单元格相应协议:didSelectRowAtIndexPath

  • 反选单元格相应协议:didDeselectRowAtIndexPath

代码实现:

#import "SceneDelegate.h"
#import "ViewController.h"
@interface SceneDelegate ()

@end

@implementation SceneDelegate


- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    self.window.frame = [UIScreen mainScreen].bounds;
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];
    self.window.rootViewController = nav;
}
@end
//ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
    UITableView *_tableView;
    NSMutableArray *_arrayData;
    UIBarButtonItem *_btnEdit;
    UIBarButtonItem *_btnFinish;
    UIBarButtonItem *_btnDelete;
    BOOL _isEdit;//设置编辑状态
}

@end
//ViewController.m
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.tableHeaderView = nil;
    _tableView.tableFooterView = nil;
    [self.view addSubview:_tableView];
    _arrayData = [[NSMutableArray alloc] init];
    for (int i = 1; i < 20; i++) {
        NSString *str = [NSString stringWithFormat:@"A %d", i];
        [_arrayData addObject:str];
    }
    //当数据源发生变化时,更新数据视图重新加载数据(刷新表格)
    [_tableView reloadData];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _arrayData.count;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;//默认组数为1
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *strID = @"ID";
    UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:strID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:strID];
    }
    cell.textLabel.text = [_arrayData objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = @"子标题";
    NSString *str = [NSString stringWithFormat:@"/Users/mac/Desktop/%ld.jpg", (long)(indexPath.row % 8 + 1)];//余数循环,使图片重复使用
    UIImage *image = [UIImage imageNamed:str];
    cell.imageView.image = image;
    [self createBtn];
    return cell;
}
-(void)createBtn {
    _isEdit = NO;
    _btnEdit = [[UIBarButtonItem alloc] initWithTitle:@"编辑" style:UIBarButtonItemStylePlain target:self action:@selector(pressEdit)];
    _btnFinish = [[UIBarButtonItem alloc] initWithTitle:@"完成" style:UIBarButtonItemStylePlain target:self action:@selector(pressFinish)];
    _btnDelete = [[UIBarButtonItem alloc] initWithTitle:@"删除" style:UIBarButtonItemStylePlain target:self action:@selector(pressDelete)];
    self.navigationItem.rightBarButtonItem = _btnEdit;
}
-(void)pressEdit {
    _isEdit = YES;//标记当前进入编辑模式
    self.navigationItem.rightBarButtonItem = _btnFinish;
    [_tableView setEditing:YES];//进入编辑状态
    self.navigationItem.leftBarButtonItem = _btnDelete;
}
-(void)pressFinish {
    _isEdit = NO;
    self.navigationItem.rightBarButtonItem = _btnEdit;
    [_tableView setEditing:NO];
    self.navigationItem.leftBarButtonItem = nil;
}
-(void)pressDelete {
    
}
//单元格显示效果协议
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewCellEditingStyleDelete;//默认为删除状态
    //return UITableViewCellEditingStyleDelete| UITableViewCellEditingStyleInsert;
}
//通过手指滑动实现删除按钮功能
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    [_arrayData removeObjectAtIndex:indexPath.row];
    [_tableView reloadData];//更新
    NSLog(@"删除");
}
//选中单元格时执行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"选中单元格!%lu, %lu", indexPath.section, indexPath.row);
}
//在已经选择单元格的情况下,再选择另一个单元格时取消上一个单元格
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"取消选中单元格!%lu, %lu", indexPath.section, indexPath.row);
}
@end

运行结果:

在这里插入图片描述

在这里插入图片描述