UIButton基础知识和自定义详解

article/2025/10/2 15:11:18

UIButton是我们经常用的UI控件,继承UIControl。这里将对UIButton的基本使用方法和自定义UIButton进行详细介绍。

一、UIBUtton基本知识介绍

对于我们学习一个新的控件、无外乎两种方法。第一种是在xcode中的.m文件查看该控件的属性和相关方法,第二种直接在storyBoard中拖入该控件,然后在Xcode中的属性检查器面板,查看该控件属性。这里选择第二种,形象生动。

这里写图片描述

无论学习什么控件,都需要把该控件的基本属性熟记于心。

1、Type
主要有以下几种
这里写图片描述

  • Custom
    该属性允许开发者设计按钮外观、而不是系统默认样式,比如要自定义Button样式。

  • System
    该属性使得按钮呈现默认风格。

  • Detail Disclosure
    该属性使得按钮呈现一个详情图标。这里写图片描述

  • InfoLight
    该属性使得按钮呈现一个图标,同Detail Disclosure。

  • InfoDark
    该属性使得按钮呈现一个图标,同Detail Disclosure。
  • ContactAdd
    该属性使得按钮呈现一个图标。这里写图片描述

总结:在我们使用Button过程中,一般直接选用Custom。

2、State Config
该属性用来设置按钮的的状态。按钮总共有4种状态:

默认状态:UIControlStateNormal

高亮状态:UIControlStateHighlighted
对应属性:highlighted

选中状态:UIControlStateSelected
对应属性:selected

禁用状态:UIControlStateDisabled
对应属性:enabled

备注:高亮和选中状态的区别:高亮指的是用户碰触该按钮的时候,按钮显示高亮效果、用户离开时按钮高亮效果消失;选中状态是需要开发者设置selected属性为YES时,才会展示某种效果、设置为NO时,效果消失。

3、Title
标题、默认使用Plain、不过多解释

4、Font
设置文本标题字体大小、不过多解释

5、TextColor
设置文本标题颜色、不过多解释

6、Shadow Color
文本标题阴影颜色、需要设置Shadow Offset

7、Image
如果给按钮设置image,前面设置的Title属性可能会不起作用,该按钮只会显示一张图片。如果还想显示文字,则需要设置titleEdgeInsets和imageEdgeInsets属性<后面的自定义button会详细讲解>。

8、Backgroud
设置按钮的背景图片、不会遮挡标题文字

9、Shadow Offset
设置阴影文本与正常文本的偏移量

这里写图片描述

Width:大于0,阴影文本相对右偏移,反之,左偏移
Height:大于0,阴影文本相对下偏移,反之,上偏移

10、Line Break
对button文本标题的截断、因为标题太长、显示不全。有以下三种模式:
Truncate Head : 对字符串多余对开头部分截断,用…代替截断部分。
Truncate Middle : 对字符串多余的中间部分截断,用…代替截断部分。
Truncate Tail : 对字符串多余的结尾部分截断,用…代替截断部分。

11、Edge和Inset
二者结合在一起使用才有意义、不是孤立存在的。
Edge指的是边界,Inset指的是边界间距。
下面自定义Button会详细介绍到。

二、改变button中图片和文字的位置

默认情况下设置按钮的图片和文字,展示效果是图片在右边、文字在左边。
相关代码如下:

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"qq.jpg"] forState:UIControlStateNormal];
[btn setTitle:@"title" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
btn.layer.borderColor = [UIColor redColor].CGColor;
btn.layer.borderWidth = 1.0;
btn.frame = CGRectMake(100, 100, 200, 200);
[self.view addSubview:btn];

效果图如下:

这里写图片描述

接下来将设置图片上文字下、图片左文字右、图片下文字上三种模式。

我们会通过两种方法进行,第一种方法:通过UIEdgeInsets设置边距;第二种方法通过完全自定义的方法。最后,我们将会对比这两种方法的优缺点。

方法一:通过UIEdgeInsets设置边距

1、UIEdgeInsets设置边距
首先需要介绍UIEdgeInsets

typedef struct UIEdgeInsets {CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} UIEdgeInsets;  

这里的四个参数表示距离上边界、左边界、下边界、右边界的距离,默认都是零。此时图片和文字在按钮的正中间。

以前在设置这个参数时,有一个误解,比如说设置按钮图片的上边距为60、即UIEdgeInsetsMake(60, 0, 0, 0)。一直以为是下面这种效果:

这里写图片描述

其实应该是这种效果:top、left、bottom、right是相对于原来的位置而言的。

这里写图片描述

疑问:设置距离上边距60,为什么这里标注30???
解答:其实这个和按钮的contentHorizontalAlignment、contentVerticalAlignment两个属性有关<下面用button的content属性代替>;默认情况下,两个属性都是AlignmentCenter,此时imageView和titleLabel两个子控件实际移动距离是设置距离的一半(60 / 2);下面会详细介绍。

小结:
top :大于0、向下移动;反之向上移动
left:大于0、向右移动;反之向左移动
bottom: 大于0、向上移动;反之向下移动
right: 大于0、向左移动;反之向右移动

2、图片右文字左模式
思路:将imageView向右平移一个自身控件的宽度、将titleLabel向左平移一个自身控件宽度。

代码如下:

//3、获取控件宽度
CGFloat imageViewWidth = btn.imageView.frame.size.width;
CGFloat titleWidth = btn.titleLabel.frame.size.width;//4、设置内边距
[btn setImageEdgeInsets:UIEdgeInsetsMake(0, imageViewWidth, 0, 0)];
[btn setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 0, titleWidth)];

效果如下:

这里写图片描述

此时效果图并不是我们想要的,就像上面提到的,这里是由于button的两个特殊属性引起的,此时需要将设置距离=自身宽度 * 2。

//4、设置内边距
[btn setImageEdgeInsets:UIEdgeInsetsMake(0, imageViewWidth * 2, 0, 0)];
[btn setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 0, titleWidth * 2)];

实际效果如下:
这里写图片描述

疑问:如果设置button中的两个属性为其它模式,移动距离是否还是设置距离的2倍???

解答:经过一翻实验发现:
a、如果将contentHorizontalAlignment设置为UIControlContentHorizontalAlignmentCenter

左右移动距离 = 设定距离 / 2;
其它模式下左右移动距离 = 设定距离。

b、如果将contentVerticalAlignment设置为UIControlContentVerticalAlignmentCenter

上下移动距离 = 设定距离 / 2;
其它模式下上下移动距离 = 设定距离。

另外,如果将imageView和titleLabel设置为以下四种情况,则在当前位置,二者无法进行左右互换:

这里写图片描述

下面写的代码默认button的content属性为center。

为了方便理解,可以下载代码,详情地址见文章最后。

3、图片上文字下模式
思路:将imageView向上移动(自身高度/2)、向右移动(自身宽度/2);将titleLabel向下移动(自身高度/2)、向左移动(自身宽度/2)。

//3、获取控件宽度
CGFloat imageViewWidth = btn.imageView.frame.size.width;
CGFloat imageViewHeight = btn.imageView.frame.size.height;CGFloat titleWidth = btn.titleLabel.frame.size.width;
CGFloat titleHeight = btn.titleLabel.frame.size.height;//4、设置内边距
[btn setImageEdgeInsets:UIEdgeInsetsMake(0, imageViewWidth, imageViewHeight, 0)];
[btn setTitleEdgeInsets:UIEdgeInsetsMake(titleHeight, 0, 0, titleWidth)];

效果如下图:
这里写图片描述

4、图片下文字上模式
思路:将imageView向下移动(自身高度/2)、向右移动(自身宽度/2);将titleLabel向上移动(自身高度/2)、向左移动(自身宽度/2)。

//3、获取控件宽度
CGFloat imageViewWidth = btn.imageView.frame.size.width;
CGFloat imageViewHeight = btn.imageView.frame.size.height;CGFloat titleWidth = btn.titleLabel.frame.size.width;
CGFloat titleHeight = btn.titleLabel.frame.size.height;//4、设置内边距
[btn setImageEdgeInsets:UIEdgeInsetsMake(imageViewHeight, imageViewWidth, 0, 0)];
[btn setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, titleHeight, titleWidth)];

效果图如下:

这里写图片描述

补充:默认情况下,imageView和titleLabel控件二者中心点与UIButton的中心点重合。

5、此方法使用局限性
当图片过大的时候,当前按钮只会显示一张图片;因为我们开发人员无法控制按钮内部的imageView和titleLabel两个子控件大小、只能移动位置。
这里写图片描述

解释:
1.当button.width < image.width时,只显示被压缩后的图片,图片是按fillXY的方式压缩。

2.当button.width > image.width,且 button.width < (image.width + text.width)时,图片正常显示,文本被压缩。

3.当button.width > (image.width + text.width),两者并列默认居中显示,可通过button的属性contentHorizontalAlignment改变对齐方式。

方法二、自定义Button
这个也可以分为以下两种方式:

a、重写系统Rect方法

思路:设置内部imageView子控件的高度=宽度=当前按钮的高度;设置内部titleLabel的高度=当前按钮的高度、宽度=(当前按钮的宽度 - 当前按钮的高度)。

备注:这里可以随意设置imageView和titleLabel的frame,这里只是提供一种思路。这个方法不是本文重点,重点是第二种方式。

重写系统的两个方法
-(CGRect)titleRectForContentRect:(CGRect)contentRect
-(CGRect)imageRectForContentRect:(CGRect)contentRect

代码如下:

//返回标题的区域
- (CGRect)titleRectForContentRect:(CGRect)contentRect
{CGFloat titleWidth = contentRect.size.width - contentRect.size.height;return CGRectMake(0, 0, titleWidth, contentRect.size.height);
}//返回图片等区域
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{CGFloat imageHeight = contentRect.size.height;CGFloat imageWidth = imageHeight;return CGRectMake(contentRect.size.width - imageWidth, 0, imageWidth, imageHeight);
}

b、重写系统layoutSubviews方法

思路:这个方法主要是重写系统的layoutSubviews、重新设置imageView、titleLabel、button自身宽高。

这里对按钮重新进行了封装,方便使用,详情见demo。

三、扩大Button的响应区域
项目中遇到一个需求,那就是不改变按钮的大小,但是响应区域要变大。经过一翻查找,终于找到了解决办法,特此记录一下。

主要利用ios的事件响应链,重写系统方法、拦截事件,扩大响应区域。

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden){return [super pointInside:point withEvent:event];}CGRect relativeFrame = self.bounds;CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);return CGRectContainsPoint(hitFrame, point);
}

详细见gitHub代码:
https://github.com/yscMichael/CustomizeButton

参考文献:
https://www.jianshu.com/p/0fc2bc2a034c


http://chatgpt.dhexx.cn/article/sNIJ7q1E.shtml

相关文章

UI基本控件(二):UIButton

UIButton——按钮 作用&#xff1a;用户交互的主要控件&#xff0c;有六种类型&#xff0c;其中自定义类型使用最为普遍 属性&#xff1a; title属性&#xff1a;是按钮的文字 titleColor属性&#xff1a;是按钮的颜色 image属性&#xff1a;是按钮显示的图像 提示&#…

oracle 用impdp 导入dmp文件

百度整理如下 /*分为7步 */ /*第1步&#xff1a;创建临时表空间(注意&#xff1a;D:\Project\OracleTableSpace\FHADMIN\ 手动创建路径) */ create temporary tablespace C##FHADMIN_TEMP tempfile D:\Project\OracleTableSpace\FHADMIN\C##FHADMIN_TEMP.dbf size 50m a…

★Oracle imp/impdp 导入dmp文件到数据库

使用EXPDP和IMPDP时应该注意的事项&#xff1a; EXP和IMP是客户端工具程序&#xff0c;它们既可以在客户端使用&#xff0c;也可以在服务端使用。 EXPDP和IMPDP是服务端的工具程序&#xff0c;他们只能在ORACLE服务端使用&#xff0c;不能在客户端使用。 IMP只适用于EX…

Oracle:使用Impdp导入dmp文件的详细过程

完全转载自&#xff1a;https://www.cnblogs.com/afei1013/p/13123784.html 这一天为了导入这个Oracle的dmp文件&#xff0c;简直就是血泪史&#xff0c;因本人对Oracle并不是很会&#xff0c;随意踩了很多小白会踩的坑&#xff0c;因此特意记录一下过程&#xff0c;防备下次的…

Oracle expdp/impdp工具使用

Oracle数据泵 一、数据泵的作用&#xff1a; 1.实现逻辑备份和逻辑恢复 2.在数据库用户之间移动对象 3.在数据库之间移动对象 4.实现表空间转移 二 、数据泵的特点与传统导出导入的区别 1.EXP和IMP是客户段工具程序&#xff0c; EXPDP和IMPDP是服务端的工具程序 2.EXP和IMP效率…

impdp导入dmp文件

impdp命令在cmd下直接用&#xff0c;不必登录oracle。只能导入expdp导出的dmp文件。 expdp导出的时候&#xff0c;需要创建 DIRECTORY 导出什么表空间&#xff0c;导入也要什么表空间。 导出什么用户&#xff0c;导入也要什么用户。 如果没有要新建。 从杭州服务器expdp导出…

解决:impdp导入.dmp文件

首先创建一个新用户&#xff0c;保证和导出dmp文件时的用户名&#xff0c;密码以及对应的表空间名相同。 不多说&#xff0c;直接上图 &#xff08;基本只用改下面备注中文部分&#xff0c;根据需求对应替换就行&#xff09; 1.创建用户 create user 用户 identified by 口…

配置NAT实现外网主机访问内网服务器(Cisco)

假设你是某公司的网络管理员&#xff0c;公司只向ISP申请了一个公网IP&#xff0c;现公司的网站在内网&#xff0c;要求在互联网也可以访问公司网站。 192.168.1.2是Web服务器的IP地址&#xff08;内网地址&#xff09;。通过分析可知&#xff0c;需要将内网服务器IP转换成外网…

内网服务器设置代理访问外网

内网服务器设置代理访问外网 背景 首先需要两台云服务器&#xff0c;其中服务器A无公网IP&#xff0c;服务器B有公网IP。 AB公网IP/110.40.255.180内网IP172.17.64.11172.17.64.17 配置A代理 直接配置环境变量即可。 cd /etc/profile.d/ vim proxy.sh进入proxy.sh文件后&…

外网访问内网的FTP服务器

1. 背景简介 最近研究如何在内网搭架FTP服务器&#xff0c;同时要保证外网&#xff08;公网&#xff09;能访问的到。终成正果&#xff0c;但走了一些弯路&#xff0c;在此记下&#xff0c;以飨后人。 2. 基础知识 FTP 使用 2 个端口&#xff0c;一个数据端口和一个命令端口…

如何让外网访问本地WEB服务器

目前很多网站开发者安装了IIS或者Apache等Web服务器&#xff0c;可以把自己电脑配置成一以路由为中心的内网服务器。 本地服务器在内网测试是绰绰有余的&#xff0c;但是有些项目需要演示给异地的客户验收&#xff0c;而又赶不及把自己的网站放到服务器上面&#xff0c;这时我们…

内网主机通过外网地址访问内网服务器

网关设备一般都具有NAT功能,分别为源地址NAT、目标地址NAT,这两种功能应用比较普遍,各厂商都有很标准的实现方式。但是对于“内网主机通过外网地址访问内网服务器”这一场景,各厂商的实现不通,下面就这个场景,分别说一下各厂商的实现。1.华为2.华三内网用户通过NAT地址访…

关于外网访问本地服务器

我在网上查到的外网访问本地服务器的方式有两种&#xff1a; 第一种是通过内网穿透的技术实现的。 第二种是通过申请域名&#xff0c;申请空间&#xff0c;申请云服务器&#xff0c;搭建网站实现的。 ①内网穿透的方式实现外网访问本地服务器 选择合适的内网穿透的工具 花…

【Linux-Windows】关于外网访问内网服务器

【Linux-Windows】关于外网访问内网服务器 1、背景2、内网穿透 1、背景 有时候&#xff0c;我们搭建的服务器系统是在内网&#xff0c;内网即内部局域网。 内网可以访问外网&#xff0c;但是外网不能访问内网&#xff0c;外网即互联网或者称为公网。 内网的设备往往组成模式如…

ssh外网访问内网服务器

现在有这样一个情况&#xff0c;实验室有自己的服务器&#xff08;内网&#xff09;&#xff0c;并且有相关老师进行维护&#xff08;公网&#xff09;&#xff0c;我们可以在内网用内网ip访问服务器&#xff0c;如果我们在家里只能通过公网进行登录。但是我在实验室有一个小服…

公网访问本地内网web服务器【内网穿透】

随着科技进步和时代发展&#xff0c;计算机及互联网已经深深融入我们的生活和工作&#xff0c;与之对应的&#xff0c;对计算机及网络的探索&#xff0c;让其为我们的生活增添色彩和乐趣&#xff0c;也成为很多人的业余爱好&#xff0c;而自行发布一个网站&#xff0c;就是这一…

搭建可通过外网访问的内网服务器

这是本人&#xff0c;第一次写博客&#xff0c;文采不好&#xff0c;技术也菜鸟&#xff0c;请不要见怪。 前一段时间裸辞了&#xff0c;于是开始广投简历。可是现实是非常的残酷&#xff0c;年底了面试的通知很少。想着是不是自己的简历上干货太少了&#xff0c;不如把自己之…

华为防火墙USG6000V---内网访问外网---外网访问内网服务器(NAT服务器)示例配置

目录 一、配置要求 二、配置步骤 1. ping通防火墙接口IP地址的条件 2. 内网ping通外网终端的条件 3. 内网ping通DMZ&#xff08;内网服务器&#xff09;的条件 三、命令解析 一、配置要求 内网可以ping通防火墙&#xff1b;内网可以访问外网&#xff1b;外网可以访问内网…

外网访问内网服务器配置方法

前提需要有固定IP 路由器必须得是通过固定IP上网&#xff0c;才能通过外网访问内网服务器 登录路由器 在地址栏输入192.168.1.1或192.168.0.1。初始密码为admin&#xff0c;若忘记密码请重置路由器。这里以TP-Link WR842N为例。 点击应用管理选择虚拟服务器 界面不一样的话&…

华为云内网服务器通过公网服务器访问外网

虽然真的太简单&#xff0c;但我还是踩坑了。尴尬&#xff0c;所以写了这个文档记录一下。 场景&#xff1a;一台没有外网的node服务器&#xff0c;通过另一台有外网的master服务器&#xff0c;来访问外网。&#xff08;这里使用的是华为云&#xff09; node192.168.0.99maste…