21、iOS 常见控件与事件处理详解

iOS常见控件与事件解析

iOS 常见控件与事件处理详解

1. UIControl 事件通知机制

UIControl 类提供了一套标准的机制来注册和接收事件。当特定事件发生时,你可以让控件通知委托类中的某个方法。

1.1 注册事件

使用 addTarget 方法来注册事件,示例代码如下:

[ myControl addTarget: myDelegate action: @selector(myActionMethod:)
    forControlEvents: UIControlEventValueChanged
];

事件可以通过逻辑或(OR)组合在一起,这样你就可以在一次调用 addTarget 时指定多个事件。

1.2 支持的事件类型
事件类型 描述
UIControlEventTouchDown 通知所有单个触摸按下事件,当用户点击屏幕或放下额外手指时触发
UIControlEventTouchDownRepeat 当点击次数大于 1 时,通知所有多点触摸按下事件
UIControlEventTouchDragInside 当触摸在控件窗口内拖动时通知
UIControlEventTouchDragOutside 当触摸在控件窗口外拖动时通知
UIControlEventTouchDragEnter 当触摸从控件窗口外拖到窗口内时通知
UIControlEventTouchDragExit 当触摸从控件窗口内拖到窗口外时通知
UIControlEventTouchUpInside 通知所有在控件内发生的触摸抬起事件
UIControlEventTouchUpOutside 通知所有源自控件外的触摸抬起事件(前提是触摸起始于控件内)
UIControlEventTouchCancel 通知所有触摸取消事件,如因手指过多或被锁定、来电中断
UIControlEventValueChanged 当控件的值发生变化时通知,用于滑块、分段控件等基于值的控件
UIControlEventEditingDidBegin 当基于文本的控件开始编辑时通知
UIControlEventEditingChanged 当基于文本的控件内文本发生变化时通知
UIControlEventEditingDidEnd 当基于文本的控件编辑结束时通知
UIControlEventEditingDidEndOnExit 当基于文本的控件通过按下返回键结束编辑时通知
UIControlEventAllTouchEvents 通知所有触摸事件
UIControlEventAllEditingEvents 通知所有基于文本的编辑事件
UIControlEventAllEvents 通知所有事件

除了默认事件,自定义控件类可以使用 0x0F000000 0x0FFFFFFF 的值范围来定义自己的事件。

1.3 移除事件动作

使用 removeTarget 方法移除一个或多个事件的动作,示例如下:

[ myControl removeTarget: myDelegate action: nil
    forControlEvents: UIControlEventAllEvents
];
1.4 获取事件动作列表
  • 使用 allTargets 方法获取控件指定的所有动作列表:
NSSet *myActions = [ myControl allTargets ];
  • 使用 actionsForTarget 方法获取给定事件目标的所有动作列表:
NSArray *myActions = [ myControl actionsForTarget: UIControlEventValueChanged ];
1.5 发送事件通知

如果你正在设计自定义控件类,可以使用 sendActionsForControlEvents 方法结束对基本 UIControl 事件或自定义事件的通知,示例如下:

[ self sendActionsForControlEvents: UIControlEventValueChanged ];
2. 分段控件(Segmented Controls)

分段控件用类似于现代厨房电器(如洗碗机或微波炉)前面板的界面取代了桌面操作系统上的单选按钮。用户看到的是一个按钮栏,按下一个按钮会使其他按钮弹出。分段控件适用于为一个选项提供有限数量的相关选择。

2.1 创建控件

使用 initWithItems 方法初始化分段控件,并设置其样式,示例代码如下:

UISegmentedControl *segmentedControl = [ [ UISegmentedControl alloc ]
     initWithItems: nil
];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

分段控件有三种不同的样式可供选择:
| 样式 | 描述 |
| — | — |
| UISegmentedControlStylePlain | 带有灰色边框的大白色按钮,适用于偏好设置单元格 |
| UISegmentedControlStyleBordered | 带有黑色边框的大白色按钮,适用于表格单元格 |
| UISegmentedControlStyleBar | 适用于导航栏的小按钮 |

如果使用 UISegmentedControlStyleBar 样式,还可以通过 tintColor 属性为整个控件设置色调:

UIColor *myTint = [ [ UIColor alloc ] initWithRed: 0.75
    green: 1.0
    blue: 0.75
    alpha: 1.0
];
segmentedControl.tintColor = myTint;
2.2 添加和移除分段
  • 添加带有标题的分段:
[ segmentedControl insertSegmentWithTitle: @"Bunnies"
    atIndex: 0 animated: NO
];
[ segmentedControl insertSegmentWithTitle: @"Ponies"
    atIndex: 1 animated: NO
];
  • 添加带有图像的分段:
UIImage *myBunnies = [ UIImage applicationImageNamed: @"bunnies.png" ];
[ segmentedControl insertSegmentWithImage: myBunnies
    atIndex: 0 animated: NO
];
  • 移除单个分段:
[ segmentedControl removeSegmentAtIndex: 1 animated: YES ];
  • 移除所有分段:
[ segmentedControl removeAllSegments ];
2.3 分段标题和图像操作
  • 设置分段标题:
[ segmentedControl setTitle:@"Unicorms" forSegment: 0 ];
  • 获取分段标题:
NSString *myTitle = [ segmentedControl titleForSegmentAtIndex: 0 ];
  • 设置分段图像:
[ segmentedControl setImage: [ UIImage applicationImageNamed:@"unicorns.png" ]
   forSegmentAtIndex: 0
];
  • 获取分段图像:
UIImage *myImage = [ segmentedControl imageForSegmentAtIndex: 0 ];

控件本身不会对图像进行缩放,因此在设计按钮图像时需要确保其适合按钮空间。你可以使用 setWidth 方法手动设置分段的宽度:

[ segmentedControl setWidth: 64.0 forSegmentAtIndex: 0 ];
2.4 瞬时点击和默认分段设置
  • 瞬时点击:默认情况下,分段控件会保留所选按钮,直到选择另一个按钮。你可以通过将 momentary 属性设置为 YES 来改变这种行为,使按钮在按下后不久自动释放:
segmentedControl.momentary = YES;
  • 设置默认分段:默认情况下,除非你指定,否则不会选择任何分段。通过设置 selectedSegmentIndex 属性来设置默认分段:
segmentedControl.selectedSegmentIndex = 0;
2.5 显示和读取控件
  • 显示控件:
// 添加到父视图
[ parentView addSubview: segmentedControl ];
// 添加到导航栏
self.navigationItem.titleView = segmentedControl;
  • 读取控件:通过读取 selectedSegmentIndex 属性来获取当前选中的分段编号:
int x = segmentedControl.selectedSegmentIndex;

为了在按钮被按下时接收通知,可以使用 addTarget 方法为 UIControlEventValueChanged 事件添加动作:

[ segmentedControl addTarget: self action:
    @selector(controlPressed:)
    forControlEvents: UIControlEventValueChanged
];

动作方法示例:

-(void) controlPressed: (id)sender
{
    UISegmentedControl *control = (UISegmentedControl *) sender;
    if (control == mySegmentedControl) {
        int x = control.selectedSegmentIndex;
        // 响应分段变化的额外代码
    }
}
3. 开关(Switches)

开关控件取代了复选框,用于打开和关闭功能。它是最容易使用的控件之一,但仍可以在一定程度上进行自定义。

3.1 创建控件

使用 initWithFrame 方法初始化开关,框架的大小会被忽略,开关会自行确定最佳大小。示例代码如下:

UISwitch *switch = [ [ UISwitch alloc ]
    initWithFrame: CGRectMake(170.0, 5.0, 0.0, 0.0)
];
3.2 替代颜色(未文档化 API)

虽然 SDK 不支持,但你可以使用未文档化的 setAlternateColors 方法将破坏性开关在激活时设置为亮橙色警告颜色,而不是标准的蓝色。不过要注意,这个 API 随时可能更改,使用未文档化 API 可能导致应用被 App Store 拒绝,因此在投入生产前要移除该功能:

[ switch setAlternateColors: YES ];
3.3 显示和读取开关
  • 显示开关:
// 添加到父视图
[ parentView addSubview: switch ];
// 添加到导航栏
self.navigationItem.titleView = switch;
  • 读取开关位置:通过 on 属性获取开关的布尔值,以确定开关是否被激活:
BOOL switchPosition = switch.on;
  • 激活开关:使用 setOn 方法在代码中激活开关:
[ switch setOn: YES animated: YES ];

为了在开关被切换时接收通知,使用 addTarget 方法为 UIControlEventValueChanged 事件添加动作:

[ switch addTarget: self action: @selector(switchStatusChanged:)
    forControlEvents:UIControlEventValueChanged
];

动作方法示例:

-(void) switchStatusChanged: (id)sender
{
    UISwitch *control = (UISwitch *) sender;
    if (control == mySwitch) {
        BOOL on = control.on;
        // 响应开关状态的额外代码
    }
}
4. 滑块(Sliders)

滑块提供了一个可视化的范围,用户可以通过滑动条来改变值,并且可以进行配置以适应不同的数值范围。它适用于呈现具有大(但不精确)数值范围的选项,如音量设置、灵敏度控制等。

4.1 创建控件

使用 initWithFrame 方法初始化滑块,框架的高度会被忽略,可以设置为 0x0

UISlider *slider = [ [ UISlider alloc ] initWithFrame:
    CGRectMake(0.0, 0.0, 200.0, 0.0)
];

设置滑块的值范围,使用 minimumValue maximumValue 属性:

slider.minimumValue = 0.0;
slider.maximumValue = 100.0;

设置滑块的默认值:

slider.value = 50.0;

滑块可以在两端显示图像,设置方法与分段控件类似。将图像复制到 Xcode 项目的 Resources 文件夹中,示例代码如下:

[ slider setMinimumTrackImage:
    [ UIImage applicationImageNamed:@"min.png" ]
    forState: UIControlStateNormal
];
[ slider setMaximumTrackImage:
    [ UIImage applicationImageNamed:@"max.png" ]
    forState: UIControlStateNormal
];

滑块有以下几种状态可供设置不同的图像:
- UIControlStateNormal
- UIControlStateHighlighted
- UIControlStateDisabled
- UIControlStateSelected

为了调试,存在一个未文档化的 API 可以在控件内显示滑块的值。使用 setShowValue 方法:

[ slider setShowValue: YES ];

同样,要注意这个 API 随时可能更改,使用未文档化 API 可能导致应用被 App Store 拒绝,在投入生产前要移除该功能。

4.2 显示和读取滑块
  • 显示滑块:
// 添加到父视图
[ parentView addSubview: slider ];
// 添加到导航栏
self.navigationItem.titleView = slider;
  • 读取滑块值:通过 value 属性获取滑块的浮点值:
float value = slider.value;

为了在滑块值发生变化时接收通知,使用 addTarget 方法为 UIControlEventValueChanged 事件添加动作:

[ slider addTarget: self action: @selector(sliderValueChanged:)
    forControlEvents:UIControlEventValueChanged
];

动作方法示例:

-(void) sliderValueChanged: (id)sender
{
    UISlider *control = (UISlider *) sender;
    if (control == mySlider) {
        float value = control.value;
        // 响应滑块值的额外代码
    }
}

如果你希望在滑块拖动时触发事件,可以将 continuous 属性设置为 YES

slider.continuous = YES;
5. 文本字段控件(Text Field Controls)

UITextField 类继承自 UIControl,可以使用 UIControl 类的许多属性来进一步定制其行为。

5.1 样式选项
属性 描述
textAlignment 指定控件内文本的对齐方式,可选值为 UITextAlignmentLeft UITextAlignmentRight UITextAlignmentCenter ,默认左对齐
borderStyle 指定文本控件周围边框的样式,可选值为 UITextBorderStyleNone UITextBorderStyleLine UITextBorderStyleBezel UITextBorderStyleRoundedRect ,默认无边界。如果使用自定义背景图像,样式将被忽略
placeholder 为空文本字段绘制一个灰色占位符字符串,当文本字段未编辑且无值时显示,接受 NSString 对象
clearsOnBeginEditing 如果用户点击文本字段时应清除文本,将此布尔值设置为 YES 。默认情况下,文本字段将光标移动到点击位置,不删除文本
adjustsFontSizeToFitWidth 当设置为 YES 时,此属性会使文本自动缩小以适应文本窗口的大小。默认行为是保留原始字体大小,允许长文本滚动出视图
background 接受 UIImage 对象并将其设置为文本字段的背景,这会使 borderStyle 属性被忽略
clearButtonMode 定义清除按钮的行为,清除按钮是文本字段右侧的小 “X” 按钮,允许用户通过点击清除所有文本。默认值为 UITextFieldViewNever ,隐藏清除按钮。可选模式有 UITextFieldViewModeNever UITextFieldViewModeWhileEditing UITextFieldViewModeUnlessEditing UITextFieldViewModeAlways
LeftView, leftViewMode, rightView, rightViewMode 这些属性允许你将 UIView 类的派生对象附加到文本字段的左右两侧。常见的是将 UIButton 对象(如放大镜或书签按钮)附加到文本字段。每个视图都有一个伴随的模式,你可以使用与 clearButtonmode 属性相同的值进行设置
5.2 渲染覆盖

除了样式选项,你可以为自定义 UITextField 对象添加许多不同的覆盖方法,这些方法会影响文本字段的渲染。这些方法返回一个 CGRect 结构,指定文本字段每个组件的边界。例如:

- (CGRect)clearButtonRectForBounds: (CGRect) bounds {
    return CGRectMake(bounds.origin.x + bounds.size.x - 50,
        bounds.origin.y + bounds.size.y - 20,
        16, 16);
}

创建 UITextField 子类时可用的覆盖方法包括:
- borderRectForBounds :指定边界矩形
- textRectForBounds :指定显示文本的边界

综上所述,iOS 中的这些常见控件和事件处理机制为开发者提供了丰富的功能和灵活性,能够满足各种不同的应用需求。通过合理运用这些控件和机制,开发者可以创建出更加交互性强、用户体验好的应用程序。

iOS 常见控件与事件处理详解

6. 控件事件处理流程总结

为了更清晰地理解控件事件的处理流程,我们可以通过以下 mermaid 流程图来展示:

graph TD;
    A[事件发生] --> B{是否注册监听};
    B -- 是 --> C[调用对应 action 方法];
    B -- 否 --> D[无操作];
    C --> E[处理事件逻辑];
    E --> F[更新 UI 或执行其他操作];

从这个流程图可以看出,当事件发生时,系统首先会检查该事件是否有注册监听。如果有注册,就会调用对应的 action 方法,在这个方法中处理事件逻辑,最后根据逻辑结果更新 UI 或执行其他操作;如果没有注册监听,则不会有任何操作。

7. 控件使用的注意事项

在使用这些常见控件时,有一些注意事项需要开发者牢记:
- 未文档化 API 的使用 :如在开关和滑块控件中提到的未文档化 API,虽然它们能提供一些额外的功能,但随时可能更改,并且使用未文档化 API 可能导致应用被 App Store 拒绝。因此,在开发过程中尽量避免使用,或者在投入生产前确保移除相关功能。
- 图像资源管理 :在分段控件和滑块控件中使用图像时,要注意将图像正确复制到 Xcode 项目的 Resources 文件夹中,以确保应用安装后能正常访问这些图像。同时,由于控件本身不会对图像进行缩放,所以在设计图像时要确保其适合按钮或控件的空间。
- 事件处理逻辑 :在为控件添加事件处理方法时,要确保逻辑清晰,避免出现逻辑混乱或错误。例如,在处理分段控件的 UIControlEventValueChanged 事件时,要正确判断控件对象,并根据选中的分段执行相应的操作。

8. 不同控件的应用场景分析

不同的控件适用于不同的应用场景,以下是一些常见的应用场景分析:
| 控件类型 | 应用场景 |
| — | — |
| 分段控件 | 当需要为用户提供有限数量的相关选择,且只能选择一个选项时,如设置主题风格(明亮模式、暗黑模式)、选择不同的分类(图片、视频、音频)等。 |
| 开关 | 用于简单的开/关功能,如开启或关闭通知、打开或关闭蓝牙等。 |
| 滑块 | 适用于需要用户调整数值范围的场景,如音量调节、亮度调节、进度控制等。 |
| 文本字段控件 | 当需要用户输入文本信息时,如登录界面的用户名和密码输入、搜索框输入关键词等。 |

9. 控件组合使用示例

在实际开发中,往往会将多个控件组合使用,以实现更复杂的功能。以下是一个简单的示例,展示了如何组合使用分段控件、开关和文本字段控件:

// 创建分段控件
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"选项1", @"选项2"]];
segmentedControl.selectedSegmentIndex = 0;
[segmentedControl addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];

// 创建开关
UISwitch *switchControl = [[UISwitch alloc] initWithFrame:CGRectMake(0, 50, 0, 0)];
[switchControl addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];

// 创建文本字段控件
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 100, 200, 30)];
textField.placeholder = @"请输入文本";
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;

// 将控件添加到父视图
UIView *parentView = self.view;
[parentView addSubview:segmentedControl];
[parentView addSubview:switchControl];
[parentView addSubview:textField];

// 分段控件事件处理方法
- (void)segmentChanged:(id)sender {
    UISegmentedControl *control = (UISegmentedControl *)sender;
    NSLog(@"选中的分段是:%ld", (long)control.selectedSegmentIndex);
}

// 开关事件处理方法
- (void)switchChanged:(id)sender {
    UISwitch *control = (UISwitch *)sender;
    NSLog(@"开关状态:%@", control.isOn ? @"开启" : @"关闭");
}

在这个示例中,我们创建了一个分段控件、一个开关和一个文本字段控件,并将它们添加到父视图中。同时,为分段控件和开关添加了事件处理方法,当用户操作这些控件时,会在控制台输出相应的信息。

10. 总结与展望

iOS 中的常见控件如分段控件、开关、滑块和文本字段控件等,为开发者提供了丰富的交互方式。通过合理使用这些控件,并结合 UIControl 的事件通知机制,开发者可以创建出功能强大、用户体验良好的应用程序。

在未来的开发中,随着 iOS 系统的不断更新和发展,这些控件可能会有更多的功能和特性。同时,开发者也可以根据实际需求,进一步扩展和定制这些控件,以满足不同用户的需求。例如,开发自定义的分段控件样式、实现更复杂的滑块交互效果等。总之,掌握这些常见控件的使用和事件处理机制,是 iOS 开发中非常重要的基础。

内容概要:本文介绍了一种基于蒙特卡洛模拟和拉格朗日优化方法的电动汽车充电站有序充电调度策略,重点针对分时电价机制下的分散式优化问题。通过Matlab代码实现,构建了考虑用户充电需求、电网负荷平衡及电价波动的数学模【电动汽车充电站有序充电调度的分散式优化】基于蒙特卡诺和拉格朗日的电动汽车优化调度(分时电价调度)(Matlab代码实现)型,采用拉格朗日乘子法处理约束条件,结合蒙特卡洛方法模拟大量电动汽车的随机充电行为,实现对充电功率和时间的优化分配,旨在降低用户充电成本、平抑电网峰谷差并提升充电站运营效率。该方法体现了智能优化算法在电力系统调度中的实际应用价值。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事新能源汽车、智能电网相关领域的工程技术人员。; 使用场景及目标:①研究电动汽车有序充电调度策略的设计仿真;②学习蒙特卡洛模拟拉格朗日优化在能源系统中的联合应用;③掌握基于分时电价的需求响应优化建模方法;④为微电网、充电站运营管理提供技术支持和决策参考。; 阅读建议:建议读者结合Matlab代码深入理解算法实现细节,重点关注目标函数构建、约束条件处理及优化求解过程,可尝试调整参数设置以观察不同场景下的调度效果,进一步拓展至多目标优化或多类型负荷协调调度的研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值