摘要

拿来即用短时间效率虽然挺高的,但是拿来的东西没有消化一次,就无法得心应手的使用它。

这次的探索思路就是,查询官方文档,设置不同的值测试单个方法中参数的变化,之后测试两个方法的执行顺序,处理的思路,最后思考总结。

在总结方法的处理逻辑时,使用伪代码的方式梳理方法的执行思路。避免解释文本太多,增加理解的成本。

最近在学习小程序开发,接触到 flex 方式布局,很喜欢这种快速和方便的方式。所以当遇到一个页面上居中显示文本的需求的时候,就想直接在 UIlabel 上处理,然后在UIlabel上设置它的内边距(类似 flex 布局)。而不是先放一个 View。然后在这个view 上放置一个 UILabel 控件,通过设置 UILabel 控件距离父 View 的距离实现。

先看代码实现,下面的代码,是搜索之后的解决方式,如果只是拿去使用,直接复制到项目中即可。需要在设置text前设置textInsets

class SHLabel: UILabel {

   var textInsets: UIEdgeInsets = .zero

   override func drawText(in rect: CGRect) {
       super.drawText(in: rect.inset(by: textInsets))
   }
   
   override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
       
       let insets = textInsets
       var rect = super.textRect(forBounds: bounds.inset(by: insets), limitedToNumberOfLines: numberOfLines)
       
       rect.origin.x -= insets.left
       rect.origin.y -= insets.top
       rect.size.width += (insets.left + insets.right)
       rect.size.height += (insets.top + insets.bottom)
       return rect
   }
}

为什么这种方式可以实现内边距?

接下来是梳理一下,为什么这样实现。首先查看开发者文档,看代码块中这两个方法是做什么的

函数 drawText(in rect: CGRect) textRect(forBounds bounds:, limitedToNumberOfLines numberOfLines) -> CGRect
标题 rect的区域中绘制文本或者阴影 返回文本的绘制的 rect 区域
详细 如果需要修改 label 中的绘图行为,需要重写这个方法。这个方法已经配置用于绘图的默认环境和文本颜色,在重写的方法中,可以自定义绘制方法,然后调用super或者自己进行绘图。 在系统执行其他文本计算之前重写这个方法(这个太难理解),如果调用 sizeToFit()sizeThatFits(_:)会触发这个方法
链接 https://developer.apple.com/documentation/uikit/uilabel/1620527-drawtext https://developer.apple.com/documentation/uikit/uilabel/1620545-textrect

之后验证这两个方法的执行顺序,和各自的作用时,发现当 UILabel 的 text 赋值时,会首先调用textRect方法,之后drawText方法被调用。

textRect在当文本rect的实际宽度大于设置UILabel的实际宽度时,会再次被调用,当然drawText也是在textRect两次调用之后被调用。

textRect 的作用

看到这里,似乎可以理解开发者文档中提到的在系统执行其他文本计算之前重写这个方法了。这个方法的作用就是先获取 UILabel 的 bounds 和 text 的行数,通过调用 super 方法计算出 text 的 rect 区域,返回给系统。

经过多次测试验证发现执行逻辑(伪代码):

  // frame 是设置 UIlabel 时的 frame
  if numberOfLines == 1 {
      textRect 被调用
      return retc 的 width = text 的 widht
  } else {
      if text 文本的 width < frame 的 width {
          text Rect 被调用
          return retc 的 width = text 的 widht
      } else {
          text Rect 被调用两次后
   
          以 frame 的 wdith 为限制,计算出 text 的 height
          return rect 的 size = (frame 的 width,text 的 height)
      }
  }

drawText 的作用

drawText中的rect参数,就是textRect方法返回的rect。text文本的实际绘制区域就通过重写drawText方法,并在其中调用它的super方法实现。

经过多次验证,这里的rect并不完全是textRect方法中返回的rect。它们之间的关系是(伪代码):

  // frame 是设置 UILabel 的 frame
  // rect 是 `textRect` 返回的
  dx = frame.x
  dy = frame.y
  if frame.width 确定不变 {
      dwidth = frame.width
  } else {
      dwidth = rect.width
  }
  if frame.height 确定不变 {
      dheight = frame.height
  } else {
      dheight = rect.width
  }
   
  return drawText 中的 rect(dx,dy,dwidth,dheight)

再问:为什么用这种方式实现内边距?

耐心看完这两个方法之后,对题目中的问题,多少有些思路了。那么就理顺一下这个思路。

首先确定一个共识,就是设置UILabel的内边距,是确定UILabel的frame区域里面,调整text的显示区域。有了这个共识,接下来就好办了。

  • 第一步就要用textRect方法获取到text的显示区域,默认text的显示区域和UILabel的bounds区域是一样的

  • 那就需要和咱们自己设置的内边距值计算获取到新的text文本的rect区域。

  • 最后就用drawText方法重新绘制一下text的rect区域显示。

那么为什么要用这种方式实现呢?因为目前只有这两个方法和 text 文本直接有关系。

优化

理论搞了这么多,到了输出一些干货的时候了。

如果,UILabel的frame已经确定了,重要的是width和height确定了。那么textRect方法就可以不用重写。

class SHLabel: UILabel {

   var textInsets: UIEdgeInsets = .zero
   override func drawText(in rect: CGRect) {
       super.drawText(in: rect.inset(by: textInsets))
   }
}

这里就可以看出,当UILabel的height不确定时,重写textRect来帮忙确定UILabel的高度。经过验证下来,这个方法中的 x 和 y 也是不用处理的,什么时候会用到它?我目前还没有遇到。

class SHLabel: UILabel {

   var textInsets: UIEdgeInsets = .zero

   override func drawText(in rect: CGRect) {
       super.drawText(in: rect.inset(by: textInsets))
   }
   
   override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
       let insets = textInsets
       var rect = super.textRect(forBounds: bounds.inset(by: insets), limitedToNumberOfLines: numberOfLines)

//       rect.origin.x -= insets.left
//       rect.origin.y -= insets.top
       rect.size.width += (insets.left + insets.right)
       rect.size.height += (insets.top + insets.bottom)
       return rect
   }
}

扩展

文章到这里,就结束了。如果你是一个细节控,感觉textRect在需要还是不需要的时候都被调用。drawText方法,不管设置还是不设置内边距也总是被调用,会不会影响性能啊?

这里提供两个方法解决:

  1. 可操作性的,就是尽量考虑需求,在不得不用的时候再使用

  2. 心理安慰性质的,那就是放下。细想一下,这两个方法都是重写的方法,重写的本质是什么?那就是不执行自己的方法,执行重写的方法。换句话说,就算系统不走重写的方法,也要走自己的方法。而这些代码对性能的影响,不值一提。

新发现

突然之间,有没有发现,咱们似乎也明白了,为什么UILabel不用固定它的height,它就可以自己确定高度,完全展示 text文本?你想.你细想...

Swift- 设置 UILabel 内边距的更多相关文章

  1. 可以简易设置文字内边距的EdgeInsetsLabel

    可以简易设置文字内边距的EdgeInsetsLabel 最终效果: 源码: EdgeInsetsLabel.h 与 EdgeInsetsLabel.m // // EdgeInsetsLabel.h ...

  2. W3School-CSS 内边距 (padding) 实例

    CSS 内边距 (padding) 实例 CSS 实例 CSS 背景实例 CSS 文本实例 CSS 字体(font)实例 CSS 边框(border)实例 CSS 外边距 (margin) 实例 CS ...

  3. CSS.03 -- 浏览器行高、字体;盒子模型--边框、内边距、外边距

    如果此时你也在自学中,请使用 FireWorks CS6 进行切图测距等,百度一下吧~ Fireworks的基本使用 新建文件   ctrl+n 打开文件  ctrl+o 调出和隐藏标尺 ctrl+r ...

  4. 谈谈tableView的重要属性内边距

    全屏穿透效果需要做到两点 tableView的可视范围占据整个父控件(或者屏幕)--设置contentsize滚动范围. 所有的cell都可以被看到,也就是说tableView中的cell不会被导航栏 ...

  5. CSS 内边距 (padding) 实例

    CSS 内边距 (padding) 实例元素的内边距在边框和内容区之间.控制该区域最简单的属性是 padding 属性. CSS padding 属性定义元素边框与元素内容之间的空白区域.CSS 内边 ...

  6. CSS 内边距 padding 属性

    CSS padding 属性定义元素边框与元素内容之间的空白区域. ㈠padding(填充) ⑴当元素的 padding(填充)内边距被清除时,所释放的区域将会受到元素背景颜色的填充. ⑵单独使用 p ...

  7. 【Swift】UILabel 设置内边距

    前言 对应一个曾经开发 Android 的人来说,没有这些基础属性简直令人发指,还是表喷这个,认真写代码 - - # 声明 欢迎转载,但请保留文章原始出处:) 博客园:http://www.cnblo ...

  8. iOS 设置UILabel 的内边距

    iOS 设置UILabel 的内边距 - (void)drawTextInRect:(CGRect)rect { UIEdgeInsets insets = {, , , }; [super draw ...

  9. 设置layui表格cell的内边距

    /*设置layui表格cell的内边距*/ .layui-table-cell { height: 50px !important; line-height: 50px !important; }

随机推荐

  1. PAT乙级:1061 判断题 (15分)

    PAT乙级:1061 判断题 (15分) 题干 判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过 100 的正整数 N 和 ...

  2. P2490 [SDOI2011]黑白棋

    P2490 [SDOI2011]黑白棋 题意 一个 \(1*n\) 的棋盘上,A 可以移动白色棋子,B 可以移动黑色的棋子,其中白色不能往左,黑色不能往右.他们每次操作可以移动 1 到 \(d\) 个 ...

  3. POJ3615-Floyd

    http://poj.org/problem?id=3615 因为只需要求所在路径的最大高度的最小值,而且n<=300,我们可以用floyd跑. g[i][j]=min(g[i][j],max( ...

  4. [考试总结]noip模拟7

    为啥博客园 \(\LaTeX\) 老挂???! \(\huge{\text{菜}}\) 刚开始写 \(T1\) 的时候,在看到后缀前缀之后,直接想到 \(AC\) 自动机,在画了半个 \(trie\) ...

  5. Maven之--安装nexus 私服

    开始搜索下载了,nexus3.19版本,下来之后,建立一个maven 骨架过程 quickstart,提示没有lgf4j依赖和和maven插件都没有,开始搜索什么原因,猜想是nexus没有索引,右搜索 ...

  6. ifix 自动化(Automation)错误弹窗的解决方案

    在先前ifix项目中添加了语音模块,然后概率性跳出自动化(Automation)错误弹窗,先前分析了很多种原因,从代码的冗余,编码等角度进行了优化,效果不是很理想,仍然会概率性出现.经过反反复复大约3 ...

  7. Mybatis学习笔记-分页

    为何要分页 减少数据处理量 便于前端展示数据 使用Limit分页 语法结构 SELECT * FROM user LIMIT startIndex,pageSize; SELECT * FROM us ...

  8. XCTF-simple unpack

    题目提示这是一个加壳的二进制文件,拖到exeinfope,是UPX壳. 这里我们用linux命令upx -d脱壳. 脱完壳之后拉入ida64中分析.找到main函数,可以看到这一句可以是输出flag的 ...

  9. 使用AVPro Video在Unity中播放开场视频(CG)笔记

    游戏中的开场CG(播放视频),采用的插件为AVPro Video1.x(和W的版本一致),Unity版本为2018.4.0f1 Asset Store:AVPro Video - Core Andro ...

  10. shell——sed编辑器

    目录 一.sed编辑器 1.1.sed编辑器工作流程 读取: 执行: 显示: 1.2.格式 1.3.常用选项 1.4.常用操作 1.5.替换 一.sed编辑器 sed是一种流编辑器,流编辑器会在编辑器 ...