Andy矢倉

老板 来杯董小姐 去冰 少糖 多放点小姐

曾梦想 仗贱走天涯 看尽女人和繁华


UITableViewCell - 自适应速成大法

原文地址:https://www.raywenderlich.com/129059/self-sizing-table-view-cells
转载地址:http://www.rockerhx.com/2016/12/12/2016-12-12-Self-sizing-Table-View-Cells/

最近滚回成都忙房子的事情,一直没时间更新,今天在群里开到好多人在聊很多培训机构都已经辞掉iOS培训老师,关闭iOS培训课程。这尼玛回想在乔帮主时代,开发iOS还是件可以装好几年逼,而如今,日落黄昏啊。
纵观近几年,有太多培训机构当搅屎棍进来瞎掺和贵国的全民创业,伪造简历不说,那帮菜鸟的技术真的是辣眼睛,卧槽,好像跑偏了,今天不是来申讨培训党的,我们是正经教程,还是回到主路继续开车~~~

此教程特地针对Xcode8,iOS10和Swift 3优化并完善,那么你需要使用Xode8或者更新的版本,并且需要你有AutoLayout,TableView以及Swift 3的相关知识,所以此教程是专门给IB党的,当然代码党也可以看看,思想都差不多,或者你也阔以转阵营。

I have a Table, I have a View, hing ~ , TableView

不管是老司机还是新司机,日常开发中使用UITableView来搭建应用框架的肯定都遇到过这种尴尬:辣就是在刚入门iOS开发,相信绝大部分人针对接到产品狗要做动态列表或者有动态高度的列表需求时抽出过皮带,因为那会儿主要都是手动计算,这种需求不仅费时费力,最后bug还多的一逼。
其实这种需求涉及到的技术最主要的无非就是Cell的自适应,俗话说的好,只要你掌握了TableView,大部分应用你是手到擒来的,对于TableView的基础用法和复用早在12年就烂大街了,对于Cell自适应也都是老生常谈,之所以还搞这种教程,完全是看着群里一帮傻屌每天都在无下限的讨论一些恬不知耻的话题,Cell自适应就是重灾区。对于网上的教程也好,开源库也好,已经掌握的就赶紧滚犊子,不要浪费时间了。。。

开始吧

在乔帮主时代,那是的确没办法,基本上都是通过手动计算Label和各种可变动控件的高度,而且要是考虑屏幕旋转,那就头大咯,还好那会儿屏幕没有太多尺寸。
步入库克时代,大概苹果的工程师也觉得手动计算麻烦的一逼,于是有了AutoLayout以及开放了新的Api。

Objective-C:

1
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize;

Swift:

1
func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize

利用此Api,无论你是代码党还是煞笔党,配合AutoLayout就可以搞定Cell自适应的问题,可以说是载歌载舞啊。

现在,得益于iOS8Interface Builder改进和AutoLayout的优化,只要是你的应用适配到iOS8以上,解决Cell自适应的问题那就So Easy!!!

那么接下来你需要搞定如下几个简单步骤:

  • 使用AutoLayout创建你的TableView。
  • rowHeight设置成UITableViewAutomaticDimension
  • 利用estimatedRowHeight设置一个预估高度或者自行实现预估高度的代理方法。

你先别逼逼问为什么要这么做,这是速成大法,先给你看效果才是最重要的,跟着做就行了,做完不懂再逼逼。。。

姿势One:约会

假设你有个煞笔客户,要让你做一个艺术家作品展示的应用,那么进入应用的第一个主页面肯定是个艺术家信息列表,而且是动态列表。但是客户需要的是快速实现,他不想听你逼逼动态列表好不好做,也不想看你在那里煞笔的炫耀技术,你特么乖乖实现就行了。

你可以自己从头搭建工程,当然考虑到看教程的大部分都是懒逼,这里给你们准备了一份初始工程,如果你不想搞事,劝你还是老实点用我这份比较好,Understand。。。



接下来我们就可以开始涨姿势了,Let’s go…

姿势Two:前戏

由于我已经帮你模拟并拆解了需求,了解并分析你就免了,跟着看就成。。。
打开Artistry工程,找到Views分组,点开Main.storyboard



从左到右分别是:

  • 作为根控制器的导航控制器。
  • ArtistListViewController负责显示艺术家列表。
  • ArtistDetailViewController负责显示艺术家作品以及详情。

让我们跑一盘,可以看到ArtistListViewController显示了一个艺术家的列表,我们选择第一个艺术家(Pablo Picasso),页面跳转到ArtistDetailViewController显示艺术家作品介绍:



作为一个艺术家,不应该单单只有文字的介绍,还需要图片,但是在这里我们并没看到图片信息,所以我们需要添加图片显示,那么由于图片的宽高大小并不统一,因此你不可能只用一个固定高度就能解决,而是需要基于Cell内包含内容才决定的动态高度。
那么我们接下来就在ArtistListViewController利用动态高度实现Cell的自适应。

姿势Three:神龙盖顶

想要获得Cell的正确高度,必须自定义一个Cell并且使用AutoLayout建立正确约束。
这里我们可以使用Command + N快捷键来创建文件,Cell是属于视图结构,因此最好是放到Views分组下,你要不喜欢可以随便放,做项目要保持烂习惯也是你自己遭殃。
OK,这里我们选择Cocoa Touch Class创建一个名为ArtistTableViewCell并且继承自UITableViewCell的源文件。语言项肯定是整Swift,也不需要创建Xib可是文件哈,你踏马要煞笔到怼Objc我也无话可说,那到这里我劝你赶紧Q掉。
打开我们刚才创建的ArtistTableViewCell.swift并删掉系统自动生成的方法,接着我们添加如下属性:

1
@IBOutlet weak var bioLabel: UILabel!

接着,点开Main.storyboard,在ArtistListViewController里选中TableViewCell,在Identity Inspector就是识别检查器,Command + Option + 3也可以,都是一个屌意思,把Class栏修改为ArtistTableViewCell才能跟代码绑定。



拖一个UILabel到刚才的Cell里,并把内容改为Bio,然后在Attributes Inspector属性检查器里把Lines设置为0,Label要是现实多行文字这个是必须的前提,OK,搞定之后看起就想这样:



刚才我们设置的那个Lines也就是Label对应的断行属性numberOfLines,这个我就奶妈当到底吧,这个属性是Int类型,设置几就是只显示几行,设置0就是完整显示,千万别忘了设置,不然你就是当不插电开机的煞笔好了。

接下来在Document Outline找到刚才绑定的Cell上右键,在弹出的outlet弹出列表上把ArtistTableViewCell的属性bioLabel拖到刚才弄的内容为Bio的Label上连接起来,演示如下:



是不是憋的一逼,一开始到现在咋都不说这么做是为啥,现在就告诉你为什么,让UITableViewCell动态高度的秘诀是利用AutoLayout里有内建高度的控件比如UILabel这种来把Cell撑开,这种做法的前提就是必须把约束给设置完整,不然系统就没法自动计算高度。

算了,要是不会AutoLayout,我还是给你一份快速上手教程,iOS7和iOS9两个版本,自行选择。
中文:iOS 7教程 - Storyboards Part1iOS 7教程 - StoryBoards Part2
英文:Auto Layout Tutorial in iOS 9 Part 1: Getting Started

刚才原理都明白了我们可以给Label拉约束了,在Storyboard可视化界面的直接快捷添加,只需要让Label的四边顶住Cell的四边即可,也就是给Label分别四个约束添加:

  • leading
  • top
  • trailing
  • bottom



关于Constraints to margins这个选项这里还是说一下,这个选项是留边的做法,最开始是Size Class这个特性考虑不全,认为所有人都会为同一个应用在Storyboard里掺杂各种屏幕的页面设计,也就是iWatch的诞生才引入的Size Class特性。
但是大部分日常开发都是基于iPhone,然后才是iPad,iWatch,所以这个作为默认勾选在iPhone上是很多新手踩的大坑,如果没注意又不知道意思,勾选之后在不同屏幕上看到的边缘就会有留边或者被裁掉的问题。
这里只是演示教程,无所谓,实际开发的时候根据需求确定勾选与否即可。

所以根据你Xcode的版本,总之就是取消勾选个Constraints to margins,然后上下左右四个约束的约束值:

  • 上下约束值为0
  • 左右约束值为8

那么我们来验证一下上面做的是否满足AutoLayout的标准:

  • 控件的起始点和宽高都能确定吗?有上下左右四边约束,这是绝逼能确定起始点和宽高的,通过。
  • 可变控件是否把Cell的内容视图contentView给顶住?Label上下两边的约束就足够顶住,通过。

所以这个自动布局完全可以确定高度以便让Cell高度自适应。

现在你的ArtistTableViewCell视图已经搞定,让我们跑一盘来看看:



不对啊,咋还是之前的屌样,What the fuck!???莫紧张,我们只是弄了可视化绑定部分,你以为还真的全程可视化啊,当然还是写点小代码,别太天真了骚年!!!

姿势Four:倒挂金钩

只有正确的配置好我们自定义的Cell才能准确计算高度。
so,打开ArtistListViewController.swift,替换掉方法tableView(_:cellForRowAtIndexPath:),代码搞成如下所示:

1
2
3
4
5
6
7
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ArtistTableViewCell
let artist = artists[indexPath.row]
cell.bioLabel.text = artist.bio
cell.bioLabel.textColor = UIColor(white: 114/255, alpha: 1)
return cell
}

这代码段应该是相当简单吧,Cell复用,设置Label的内容,设置文字颜色,还有更多需要我解释的吗!???

再次跑起来,除了文字颜色变了,其他还是一个屌样,想想,我们把bioLabel的行数限制设置为0,取消掉行数限制,约束也设置好了,但是目前TableView还没那么智能让你想啥来啥,我们还需要告诉它让AutoLayout来负责驱动Cell的高度。

点开ArtistListViewController.swift,在viewDidLoad()后面加上这两句代码:

1
2
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140

当你把rowHeight这个属性设置为UITableViewAutomaticDimension,只要保持约束正确,AutoLayout就会自动确定Cell的高度。
同时还需处理的便是estimatedRowHeight这个属性,我们这里选择140这个值,只是在这个项目当中大概估算出来的,每个项目或者每个列表都需要自行处理这个值,按照语义,预估一个中间值,这样减少刷新的性能消耗。

好,我们再来跑跑看,列表是不是看起来就完美了😉😉😉



姿势Five:老树盘根

虽然现在应该可以完整显示了,但是任然很挫,我们需要添加一些图片和艺术家大名来美化一下看起来才像样。
那么我们来给ArtistTableViewCell添加图片和艺术家名字,点开ArtistTableViewCell.swift添加如下代码:

1
2
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var artistImageView: UIImageView!

我们添加了一个名为artistImageView的图片属性,然后在可视化里面绑定图片控件即可。

点开Main.storyboard,选到艺术家列表里的Cell,点到Size Inspector尺寸识别器里,把Row Height设置为140,这样方便给你更多控件来添加新的控件。



接下来我们需要改变布局和约束,切换到Document Outline里点开Content ViewConstraints,或者在Size Inspector尺寸识别器里也可以,反正就是要删掉BioLabelleading约束。



直接干掉Label左边的约束后会看到Auto Layout的警告,暂时忽略掉,我们最后会把约束给完善好。
现在拉住Label的左边缘,把Label左边距拖到Cell中间位置,我们需要把左半部分留出来显示图片和艺术家的名字:



然后拖一个UILabel到Cell底部,位置大概到Cell左半部分的水平居中即可,内容改为Name



继续拖拽一个UIImageView控件到NameLabel上面:



最后,我们把新加的Label和ImageView绑定到代码上:



OK,接下来我们开始完善约束,这里我们只讲关键点,就不具体手把手教你该怎么拉约束了:

  • NameLabel到Cell的ContentView底部间距为8
  • NameLabelImageView的间距为8
  • ImageView到Cell的ContentView顶部间距为8
  • ImageView左边间隙到Cell的ContentView左边间距为8
  • ImageView右边间隙到BioLabel左边间距为16

由于BioLabel的内容为动态,所以即便是ImageView的上下左右四个约束都确定了也不能确定其frame。这里我们需要把ImageView控件的宽度大概控制到屏幕宽度的一半,做法有很多,这里只介绍一种。
选中Cell的ContentViewImageView,使用约束快捷工具,给二者建立一个Equal Widths的约束即可:



不是说好的一半么,那就在Document Outline里选到刚才添加的Equal Widths约束,在Size Inspector尺寸识别器里把multiplier设置为0.5



这样的话,ImageView的宽度就只有屏幕宽度的一半了。
我们接着把剩下的约束给怼完:

  • 选中ImageViewNameLabel使用Equal Width约束让二者宽度相等。
  • 选中ImageViewNameLabel使用Horizontal Centers约束让NameLabel居中与ImageView。

那么现在我们已经把所有的约束都添加完毕,此时AutoLayout还是会报一些黄色警告。做开发以来,撸主最讨厌的就是警告,不管是编码还是可视化,必须全部消除。
Document Outline选中Content View,然后使用Resolve Auto Layout Issues快捷工具,利用All Views分栏下的Update Frames功能即可让ContentView下的所有子视图的Frame都正确更新,消除警告。



可视化部分已经完成了,那么我们开始进行编码工作。点开ArtistListViewController.swift,在tableView(_:cellForRowAtIndexPath:)方法体内的bioLabel字体颜色设置代码后面加上代码:

1
2
3
4
5
6
cell.artistImageView.image = artist.image
cell.nameLabel.text = artist.name
cell.nameLabel.backgroundColor = UIColor(red: 1, green: 152 / 255, blue: 0, alpha: 1)
cell.nameLabel.textColor = UIColor.white
cell.nameLabel.textAlignment = .center
cell.selectionStyle = .none

再次运行,这下看起来是不是很好多了,等等先别慌,继续往下滚动,到Georgia O’Keeffe你在看看是不是感觉有些奇怪。



大家看到NameLabel的纵向被拉伸了,不必精慌,我们继续完善。

点开Main.storyboard,选到NameLabel并创建一个到Cell底边的间隙约束,约束值也是8。然后在Document Outline选中刚才创建的约束,这时候你会看到刚创建的约束和之前创建的底边约束一模一样,先别慌,随便选一个,跟着做,看完你就不逼逼了。
选中约束之后切换到Size Inspector尺寸识别器里,把Relation一栏改为Greater Than or Equal



然后我们把另外一条一样的约束选中,把priority这个优先级选项值改为250



好,这里我们来解释下刚才都干了什么,两条相同的约束:

  • 一条约束关系是Greater Than or Equal大于等于关系,优先级值为1000
  • 一条约束关系是Equal等于关系,优先级值为250

熟悉AutoLayout的同学都知道,优先级为1000的是默认优先级,这个数值越大优先级就越高,那为什么要选250呢?因为苹果设计就是控件的抗压缩仿拉伸属性默认优先级的值为251,所以避免和整个控件优先级冲突那就小一点。
这样来设置约束,就是当整个Cell高度过高,NameLabel和Cell的间隙过大是舍弃那条约束值为8的约束。
现在跑一盘,看看是不是没有拉伸了?

姿势Six:直捣黄龙

还记不记得一开始我们展示,从艺术家列表点击会进入到一个对应的作品详情界面。详情页面的内容包含文字和图片,当然内容肯定是动态高度,所以我们用Cell自适应大法来搞定佢。
跟之前的第一步一毛一样,我们先创建一个UITableViewCell的子类,并命名为WorkTableViewCell,接着点开WorkTableViewCell.swift,删除自动生成的方法,为其添加下面的属性:

1
2
3
@IBOutlet weak var workImageView: UIImageView!
@IBOutlet weak var workTitleLabel: UILabel!
@IBOutlet weak var moreInfoTextView: UITextView!

点开Main.storyboard,在Artist Detail View Controller控制器下选中TableView的Cell,在Identity Inspector就是识别检查器里把Class设置为WorkTableViewCell绑定。并把row height行高设置到200方便我们拖拽控件,这里行高随意你喜欢设置,你玩着方便就行。。。
再接着我们拖动UIImageViewUILabelUITextView这三个控件如下图所示,UIImageView在上,UILabel在中,UITextView在下:



然后把TextView的内容改为Select For More Info >Label的内容改为NameImageViewContent Mode改为Aspect Fit。选中TextView,在Attribute Inspector属性识别器里把alignment改为Centered,并把Scrolling Enabled关闭,让用户不能认为滚动。



同样重要的必须记得把Label的行数限制取消,也是就Lines栏设置为0。因为TextView滚动被禁用,用户无法滚动控件,那么TextView就知道自动适应其内容。
随便禁用了用户滚动,我们还是应该考虑得更周全才行,连同User Interaction Enabled也给踏马一起关掉,这样TextView在接收到用户滚动事件的时候就不会响应,顺势就传到父视图Cell上。Cell自然而然就替你处理滚动的手势,完全不必操心。
然后我们把刚才添加的三个属性的对应连接到对应控件上,照着前面搞就阔以了,还要手把手只能告诉你还是不适合傻瓜式教程。
搞定之后那么我们就开始添加约束(切记关掉Constraints to margins):

  • TextView底边到Cell的ContentView的底边距为0
  • TextView左右两边距到Cell的ContentView的左右两边距为8
  • TextView顶边到Label的底边距为8
  • Label顶边到ImageView的底边距为8
  • Label水平居中。
  • LabelImageView保持相同宽度。
  • ImageView顶边到Cell的ContentView顶边距为0
  • ImageView左右两边距到Cell的ContentView的左右两边距为8

记得更新Frames来消除恶心的警告。那么可视化部分又搞定了,下面继续同之前的步骤一样,添加处理动态高度的代码。
点开ArtistDetailViewController.swift,把tableView(_:cellForRowAtIndexPath:)方法体内的代码改造成如下代码即可:

1
2
3
4
5
6
7
8
9
10
11
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! WorkTableViewCell
let work = selectedArtist.works[indexPath.row]
cell.workTitleLabel.text = work.title
cell.workImageView.image = work.image
cell.workTitleLabel.backgroundColor = UIColor(white: 204/255, alpha: 1)
cell.workTitleLabel.textAlignment = .center
cell.moreInfoTextView.textColor = UIColor(white: 114 / 255, alpha: 1)
cell.selectionStyle = .none
return cell
}

看起来是不是很熟悉,其实就是构建一个Cell的内容并返回,跟之前都大同小异。
那么最后我们把viewDidLoad()方法体内的代码添加一下就算是搞定了。

1
2
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300

其实就是跟之前代码类似,现在可以跑跑看,艺术家的作品详情应该是可以完美显示了吧!!!



其实这样还不算完美,更完美的是可以动态延展的内容。下面我们就来见识见识吧!!!

姿势Seven:蜻蜓点水

前面我们已经都把布局和约束全部搞定好了,接下来扩展Cell简直就易如反掌,那么我们来做更有趣的事:点击Cell可以动态显示更多内容。
点开ArtistDetailViewController.swift,添加如下extension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension ArtistDetailViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 1
guard let cell = tableView.cellForRow(at: indexPath) as? WorkTableViewCell else { return }
var work = selectedArtist.works[indexPath.row]
// 2
work.isExpanded = !work.isExpanded
selectedArtist.works[indexPath.row] = work
// 3
cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText
cell.moreInfoTextView.textAlignment = work.isExpanded ? .left : .center
// 4
tableView.beginUpdates()
tableView.endUpdates()
// 5
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}

我们都干了什么呢?



分五点,下面慢慢解释:

  • 1.通过TableView取到点击对应Cell的索引,并取出索引所对应的数据模型对象
  • 2.取反Cell是否被扩展的状态,并设置会原数组(必须这么做,因为我们建立数据结构是采用的传值而非引用,也就是这份拷贝被操作后必须重新覆盖才行)。
  • 3.这一步操作文本视图,视Cell是否被展开来改变文本内容和对齐方式。
  • 4.接着需要刷新TableView来显示新的内容。
  • 5.最后让TableView滚动到屏幕顶部位置以获得更好的用户体验。

这里还需要处理一下tableView(_:cellForRowAt:)方法,加上这两行设置代码用于刷新显示:

1
2
cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText
cell.moreInfoTextView.textAlignment = work.isExpanded ? .left : .center

这样就方式Cell被重用的时候导致文本内容的展开状态显示错误的问题。
接下来我们跑以来看看,但是呢,图片的动画貌似有点点奇怪???

无需惊慌,只需要简单修复依稀就可以了。点开Main.storyboard,选中WorkTableViewCell里的ImageView控件,切换到Size Inspector尺寸检查器,把Content Hugging PriorityContent Compression Resistance Priority这两个优先级的值改为如下图所示。



设置Vertical Content Hugging Priority为252的目的是防止纵向被拉伸导致的图像动画是拉伸过度。Vertical Compression Resistance Priority为749是为了允许其他元素扩展时能够被压缩,使其过度动画更丝滑。这里不用担心图像会被压缩,因为整个Cell的高度计算没有问题,文本被扩展之后还是有足够的空间留给图片。
那么现在我们来运行看看,试试就完美很多了呢!



姿势Eight:双龙出海

尽管我们已经做得够好了,煞笔客户还是要叨逼叨,想要加入老年人功能,意思就是我要给老年人使用我们的App,那么老年人的手机字体可大可小,系统应用里阔以随便调整,相信以你的技术水平,也就几行代码分分钟的事啦!!!
我真是去你奶奶个腿,还好是如今,要是乔帮主时代,突然接到这种需求还说分分钟的风凉话,早特么被我两脚干死了,还想叨逼叨。

iOS7中引入了Dynamic Type是的这项任务变得很简单,Dynamic Type使开发者可以为不同文本块指定不同风格,并且当用户调整首选大小时,该文本可以自动适应。
打开ArtistListViewController.swift,在tableView(_:cellForRowAt:)方法体内返回之前加上两行代码:

1
2
cell.nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
cell.bioLabel.font = UIFont.preferredFont(forTextStyle: .body)

这个是基于文本接口元素来使用Dynamic TypepreferredFont(forTextStyle:)接口仅仅只包含一个参数,系统默认提供了十种不同的类型给我们使用,想要了解更多的可以访问此接口的官方文档:preferredFont(forTextStyle:)
现在我们只需要确保TableView在首选字体大小改变的时候去刷新自己即可。在ArtistListViewControllerviewDidLoad()方法下方添加如下代码:

1
2
3
4
5
6
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange, object: .none, queue: OperationQueue.main) { [weak self] _ in
self?.tableView.reloadData()
}
}

这里我们添加了一个UIContentSizeCategoryDidChange通知观察,当首选项大小改变的时候,就会收到此通知,然后我们重载TableView,因为我们在tableView(_:cellForRowAtIndexPath:)方法体内已经处理好了preferredFont(forTextStyle:)接口,那么我们收到通知之后对应刷新列表就阔以了。

这里注意:如果是在iOS9以上就无需再控制器销毁的时候移除消息中心的观察者,以下的版本就需要多写下那行蛋疼的移除代码了。

ArtistDetailViewController页面要支持Dynamic Type也是相同的做法。打开ArtistDetailViewController.swift,在tableView(_:cellForRowAtIndexPath:)方法体内返回之前添加如下两行代码:

1
2
cell.workTitleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
cell.moreInfoTextView.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.footnote)

和刚才一样记得在ArtistDetailViewController里添加viewDidAppear(_:)的相同实现。
现在就给我跑起来,打开设置应用,操作顺序这样:通用 > 辅助功能 > 更大字体,进入首选项大小调整页面,把字体调整到最大。



有可能某个模拟器版本的问题不一定有效果,那就用真机,绝逼可以。你调整了首选项字体大小之后,在切换回应用看看,484敲开心,完全不用担心字体大小改变导致手动计算失误而产生视觉上的Bug了。



打完收工

Cell自适应大法的八大姿势已经传授完毕,最后给懒癌患者提供了最终的工程
我们整个过程操作的其实都是最简单的TableView结构,所涉及到的内容已经足够应付复杂场景,日常开发无须担心,如果遇到非常棘手的那就只能祖先保佑,自己分析拆解任务,没人能帮你。

子丹:你想学什么?
子华:刀,千日剑百日刀,学剑要一千天,学刀百多天就学会,这样快得多嘛,而且剑要磨两边,刀一边就行了,不过百多天也太久了,有没有。。。两日刀呢?
子丹:你知不知道刀的用途呢?
子华:砍戳拉切磨转抽,劈削斩刺菜干汤。。。不是,是割拖剁,这些我懂啊。
子丹:刀呢,是用来将物件分开的。
子华:我就是想学这种既简单又上乘的刀法。别只是眼巴巴地盯着我,你快说吧。
子丹:简单?怎么会有既简单又上乘这回事的?
子华:那就换转过来,说一些既上乘又简单的也可以。
子丹:有没有听过上乘的刀法分三明三暗三毒?而三毒呢又分三真三假三暗,另外还有三密,而三密又分。。。
子华:嗳。。。太上乘了。。。有没有中乘的?。。呃不,最好是速成的,速成速成速成。。。
子丹:拿起那刀试试看。
子华:你的刀这么锋利,试不到我的。
子丹:你这种是刀劈柴,不是你用刀去劈柴。
子华:我没有使劲而已。
子丹:现在可以说你用刀去劈柴了,但你没有用心去劈啊。
子华:我何只用心去劈啊,我连肺也用上了。你不想教的话就别教了,用不着长篇大论,你干脆说“刀即是我,我即是刀”吧?
子丹:确是如此啊。

———————————————————————————————————————————— 《战狼传说》

电影剧情其实很一般,主要是引用台词,引用台词的意思呢就是说,速成那都是扯卵蛋的,今天看不懂不要紧,过几天再回来瞄一遍,半个月瞄两遍相信应该就融会贯通了。。。
如有任何意见或问题,欢迎讨论!!!

最近的文章

iOS手把手搭建·无限循环滚动视图

在大多数常规App开发当中,我们都会有产品运营栏的需求,也就是列表页或者产品顶部,又或者整个页面需要展示几个滚动的运营活动、产品、广告什么的,当然,也可能是一个自己实现的一个图片浏览器。在早些年,这类需求大多都是从First逐个滚动到Last,然后再自动滚到First,技术上无非都是通过UIScrollView + Timer的方案,iOS开发往往都喜欢专注于用(xuan)户(ji)体(zhuang)验(bi)的,所以后来出现了无限循环滚动的体验。 …

Infinite, ScrollView, UICollectionView, iOS 继续阅读
更早的文章

Failed to import bridging header 的奇葩解决方案

本人是个升级控,只要有需要升级的看看日志没啥问题就开整,等了大半年的Cocoapods的1.0.0终于出正式版本了,介于0.38.0到0.39.0的痛苦过程,心里有点小担心,不过那时还好是Objective-C,问题出在配置上都可以stackoverflow解决,这次要面对这次还要多面临Swift的挑战,废话太多了,直接开整。 升级的过程还是老套路: 1sudo gem install cocoapods 老套路还是还是老问题/usr下路径权限不够,试着改变权限: 1sudo chown $(whoami):admin /usr/local && sudo chown -R $(whoami):admin /usr/local 1sudo chown $(whoami):admin /usr/bin && sudo chown -R $(whoami):admin /usr/bin …

Xcode 继续阅读