最近刚刚开始 IOS 开发,使用的是 Swift (非 swiftUI)开发,发现了一个问题,困扰了我,希望有大佬可以解答

2021-01-15 18:22:12 +08:00
 Xcution
我发现很多地方都要固定写死控件的高度,宽度?难道没有自适应的方式嘛?

我在网上搜了用 SnapKit 的方式可以不设置控件的高宽来使用,这个算是一种方式,但是用了这个方式之后,获取控件的高宽有时候为 0,好像是因为 snapKit 是异步的原因,太痛苦了,有时候就是需要高宽但是获取不到

然后还有个问题就是控件的高宽根据内容自适应,我感觉这个是最头痛的,上面的 snapkit 是和父布局的高宽拉伸,但是有时候我想根据子控件内容自动适应高宽怎么弄呀

这段时间一直在弄控件宽度高度的问题,太痛苦了,因为以前是安卓开发,安卓确实没这么麻烦,如果真的只能这样的话安卓其他不说至少在这方面还是要方便点的,我想我应该是哪里没弄对,是否有大佬解答一下,或者有相关的资料也行,我感觉我这样开发的,问题太多了
7446 次点击
所在节点    iDev
40 条回复
vincentxue
2021-01-16 03:44:06 +08:00
我来补充一下楼上的回复。准确地说,SnapKit ( Auto Layout ) 的代码只是描述了约束。Auto Layout 相比传统的手动布局多了一步更新约束的操作,对应的是 updateConstraints 方法,约束代码通常要放在这里面,约束是从下到上的,所以 super 是放在最后的。这个方法被系统调用之后,View 还没有完成布局,也没有 frame 。直到 layoutSubviews 被调用,这个时候约束才会被转换成具体的 frame,布局是从上到下的,所以要先调用 super 。Auto Layout 推出之前,都是覆盖这个方法来写 frame 的。Auto Layout 推出之后,这个方法主要是用来在布局完成之后做一些操作的。

如果你想要在布局完成之前拿到一个 View 的 size,系统给你提供了三个方法,intrinsicContentSize 、sizeThatFits: 和 systemLayoutSizeFittingSize:。这三个方法具体的区别你可以去网上查,但是通常你都不会用到它们。

Auto Layout 的原理在 WWDC 2015 的时候有几节视频专门讲了,楼主有兴趣可以去找找看一下。

我帮你找好了。https://developer.apple.com/videos/all-videos/?q=auto%20layout

如果你觉得 Auto Layout 实在是太麻烦了,你也可以用我刚才说的那个开源了 FlexLayout 的 layoutBox 的另一个叫 PinLayout 的库。它是一个用 Auto Layout 的语法去直接操作 frame 的库,没有任何约束,和直接操作 frame 没有区别。这些布局库都是可以混用的,你可以一个 View 用 Auto Layout,另一个 View 用 FlexLayout,再一个 View 用 PinLayout 。layoutBox 下的两个库我都是一直在用的,写了好几个十万行以上代码的商业 App,没有任何问题,非常的稳定。
Roccc
2021-01-16 10:04:36 +08:00
百分比+SnapKit
magic3584
2021-01-16 10:13:16 +08:00
1. 自适应可以
```` objective-c
view1.snp_makeConstraints { make in
make.height.equalTo(view2).dividedBy(2)
}
````

2. 获取 frame 之前先调用
superView.setNeedLayout()
superView.layoutIfNeeded()
magic3584
2021-01-16 10:15:57 +08:00
楼上 code block 标记 OC 但实际写的 swift...
expkzb
2021-01-16 10:52:49 +08:00
“我在网上搜了用 SnapKit 的方式可以不设置控件的高宽来使用,这个算是一种方式,但是用了这个方式之后,获取控件的高宽有时候为 0,好像是因为 snapKit 是异步的原因,太痛苦了,有时候就是需要高宽但是获取不到”

-- 如果确实需要获取宽高,执行一下 layoutIfNeeded,在后面应该就可以获取到宽高了。可能也有些细节我没提,好久没搞了。

"然后还有个问题就是控件的高宽根据内容自适应,我感觉这个是最头痛的,上面的 snapkit 是和父布局的高宽拉伸,但是有时候我想根据子控件内容自动适应高宽怎么弄呀"

-- 你不指定父视图的宽高约束就行,自然会被子视图的约束撑起来。

自动布局很好用,一定要多练习,掌握它的思维方式。唯一值得诟病的是改起来比较头疼。
loarland
2021-01-16 18:30:19 +08:00
控件自身约束要完整,互相之间的优先级也要调整
365473321
2021-01-16 18:32:05 +08:00
其实 AutoLayout 的初衷就是要你忘掉 Frame 布局,设置好约束,剩下的工作就交给 AutoLayout Engine 就好啦
如果你想要立刻获取到 frame,你需要调用两个方法
setNeedsLayout()
layoutIfNeeded()

let subView = UIView(frame: .zero)
view.addSubview(subView)
subView.snp.makeConstraints { make in
make.top.left.equalToSuperview()
make.size.equalTo(300)
}
subView.setNeedsLayout()
subView.layoutIfNeeded()
print(subView.frame)
Freeego
2021-01-16 19:10:59 +08:00
一般来说某个控件用了 AutoLayout 就不需要再获取 frame 了,这也是 AutoLayout 的目的,如果一定要获取的话就在约束之后调用父控件的 layoutIfNeeded()

父控件根据子控件自适应宽高,一句话就是通过子控件的约束计算出父控件的大小,父控件本身不设置宽高限制。多练习几次以后就会熟练了,只要你在写完子控件的约束以后,能够从中推算出父控件的 contentSize 就行,就像解方程一样。
Jackiehu
2021-01-16 19:42:50 +08:00
因为是异步绘制,需要在你获取最终 frame 前 layoutifneeded
Jackiehu
2021-01-16 19:54:42 +08:00
autolayout 就是等 layoutsubviews 后才有的 frame,layoutifneeded 会让 view 立即绘制,所以在之后可以拿到大小宽高
365473321
2021-01-16 20:00:45 +08:00
@365473321 子视图调用这两个方法可以确定子视图的 size,父视图调用这两个方法可以确定子视图的 frame
xhpan10
2021-01-16 21:57:34 +08:00
通过约束条件还有自定义 View 的 content size 来做适配啊
Xcution
2021-01-18 17:24:49 +08:00
@Freeego @expkzb 我就是遇到这种问题,比如说我用 UICollectionView,显示的网格状的,有时候有 5 排数据,有时候有 6 排数据,如果我不设置 UICollectionView 的高度内容就显示不出来,但是设置我又不知道该设置多少,还有种就是知道每个 cell 的高度来算高度进行设置,这种也行,但是万一我 cell 的高度也是动态的啦,要计算 cell 的高度就要显示出来才能计算,不设置 UICollectionView 的高度有显示不出来,所以就必须要设置 UICollectionView 的高度,但是因为是动态的我又不知道该设置多少
Freeego
2021-01-18 19:16:10 +08:00
@Xcution 你说的应该是 UICollectionView 的 contentSize 吧,frame 在初始化的时候就写死了,里面的内容多了少了影响的都是 contentSize 。而且 UICollectionView 的 contentSize 都是自动的,它就是个 UIScrollView 的子类,比如数据多了 contentSize 就会变大,如果范围超过 collectionView 的 frame.size 了就可以滑动,不需要自己去改 contentSize 。

如果又要网格布局 cell 高度又要动态变化,那就是瀑布流了,要重写一个 layout 才可以实现。

不知道你说的不设置 UICollectionView 的高度内容就不显示是什么意思,是不是 layout 的 itemSize 没有写对?不应该有"设置 UICollectionView 的高度"这种做法,frame 和内容无关,contentSize 是自动的。
kfchyc
2021-01-29 14:29:24 +08:00
在一个 controller 的不同生命周期函数中,view 的一些视觉属性可能错误或为 0,具体情况取决于你写在哪和你的需要如何
CodingIran
2021-02-01 13:09:19 +08:00
@vincentxue 害 何必折腾呢 frame 布局不香么 用上面某层的回复“手动精准控制每一个像素的位置”不香么
vincentxue
2021-02-02 02:44:25 +08:00
@CodingIran frame 布局香在哪里...
CodingIran
2021-02-02 08:55:09 +08:00
@vincentxue
1.性能好,iOS 12 之前的 AutoLayout 性能一言难尽,用 frame 尽可能的确保流畅性还是很重要的,当然一些简单的静态页面用 AutoLayout 也无妨
2.易于封装组件,道理同市面上的三方 UI 库,几乎清一色的 frame 布局,这对于自定义控件的复用和组件化有很大帮助
3.如你自己所说,“ iOS 的布局系统以及它的命令式 UI 开发体验跟 Android 比简直就是一坨屎”,所以 AutoLayout 也没有多好用,只是比以前的 frame 布局稍微“方便”一丢丢而已。等 SwiftUI 真正成熟了,最低支持版本追上来了,我立马把 frame 布局换掉,而如今我还是老老实实玩好 layoutSubviews 和 sizeThatFits: 吧 ┓( ´∀` )┏
iXInbo
2021-02-07 00:47:28 +08:00
snapKit 基于苹果的 AutoLayout,会根据屏幕自适应
使用 frame 就是先给定宽度和高度的。
如果你需要 frame 和 AutoLayout 混用,可以试试 layoutIfNeeded 先于 frame 调用。
jiahy
2021-03-25 11:14:07 +08:00
自适应布局的话可以试试 UIStackView 嵌套布局,Autolayout 自适应大小适用控件重写了 IntrinsicContentSize,自动布局的时候会根据这个来自动设置约束,本质上还是要自己计算。不过一些常用的系统控件都实现了自动计算内容大小,关键约束设置正确就不用管了,其实设置一个完美自适应的约束还是比较烦的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/745335

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX