V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
Xcution
V2EX  ›  iDev

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

  •  
  •   Xcution · 2021-01-15 18:22:12 +08:00 · 7445 次点击
    这是一个创建于 1408 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我发现很多地方都要固定写死控件的高度,宽度?难道没有自适应的方式嘛?

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

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

    这段时间一直在弄控件宽度高度的问题,太痛苦了,因为以前是安卓开发,安卓确实没这么麻烦,如果真的只能这样的话安卓其他不说至少在这方面还是要方便点的,我想我应该是哪里没弄对,是否有大佬解答一下,或者有相关的资料也行,我感觉我这样开发的,问题太多了
    第 1 条附言  ·  2021-01-17 14:00:15 +08:00
    各位大佬说的我都看了,都说得很对,非常感谢,由于刚刚才开始开发 ios,所以还比较生疏,需要继续学习,非常感谢各位
    40 条回复    2021-03-25 11:14:07 +08:00
    gdtdpt
        1
    gdtdpt  
       2021-01-15 18:25:18 +08:00
    不是专业 App 开发,只是搞过一阵子,我记得 iOS 的默认布局方式就是安卓里的约束布局,只要设置元素自身的约束、元素与元素之间的约束、元素与父元素的约束就能自适应了。
    linKnowEasy
        2
    linKnowEasy  
       2021-01-15 18:25:48 +08:00
    搜一下 SnapKit 自适应 + 你要自适应的控件.
    timelessg
        3
    timelessg  
       2021-01-15 18:30:30 +08:00 via Android
    本质还是 autolayout 的问题,首先子 view 需要有 contentsize,也就是子 view 要先把自己撑起来,其次各个子 view 之间的约束要设对,这样才会撑起来父 view
    Xcution
        4
    Xcution  
    OP
       2021-01-15 18:31:33 +08:00
    @gdtdpt 但是我用的代码进行布局的,不知道怎么弄,网上搜了也没搜到相关的资料
    @linKnowEasy 我去搜搜看
    Xcution
        5
    Xcution  
    OP
       2021-01-15 18:32:13 +08:00
    @timelessg 有相关的资料可以看嘛
    finab
        6
    finab  
       2021-01-15 18:33:33 +08:00
    布局应该不需要确切的 高度,可以把你要的布局说一说
    linKnowEasy
        7
    linKnowEasy  
       2021-01-15 18:37:16 +08:00
    搜下 SnapKit 教程 https://www.jianshu.com/p/332b816cd3e4

    这个概念其实就是 View 没有固定大小. 都是相对 superview 或者同级的 View
    cincout
        8
    cincout  
       2021-01-15 18:47:23 +08:00
    给你打了一堆字,然后发现我并不擅长给别人讲解。。。还是给你个思路吧,其实很简单,想要自适应内容的高度,你得让内容的上下约束都固定在 contentView 上,且保证内容自身有高度,如果是多行 UIlabel,得保证 UIlabel 有固定宽度。
    imkerberos
        9
    imkerberos  
       2021-01-15 18:48:38 +08:00
    FlexBox, 你值得拥有。
    everhere
        10
    everhere  
       2021-01-15 19:08:30 +08:00
    snapkit 可以设置宽高,比如
    make.width.equalTo
    make.height.equalTo
    Building
        11
    Building  
       2021-01-15 19:15:10 +08:00
    你就没有想过 Android 虽然不用写高度,那高度怎么来的?肯定还是要算的啊,子视图没有高度,那么肯定有一个高度确定的父容器,然后通过约束计算得出子视图的高度。
    ooops
        12
    ooops  
       2021-01-15 20:13:07 +08:00
    把个 AutoLayout 的教程系统的看一下吧,现在这么问没法回答。
    yikuo
        13
    yikuo  
       2021-01-15 21:19:56 +08:00 via Android
    这其实跟 snapkit 没关系,iOS 的 autolayout 布局方式就是这样的。有的地方可以使用 UIStackView 来简化约束。至于获取不到宽高,是因为你获取的时候 UI 还没计算出真正的大小,这里有两个选择:只用约束不要手动设置宽高,或者在 layoutsubview 之后再获取宽高。我个人觉得 Android 的布局比 iOS 简单很多,Android 有多种布局方式组合使用,可以专心布局,而 iOS 就只有 frame 和 autolayout 以及 UIStackView 了,需要耗费很多精力在调整位置和大小上面。
    StyxS
        14
    StyxS  
       2021-01-15 21:35:30 +08:00
    优先级和抗伸缩性
    ydatong
        15
    ydatong  
       2021-01-15 22:08:05 +08:00
    可以看看 uistackview,一般的布局能省不少事
    Baymaxbowen
        16
    Baymaxbowen  
       2021-01-15 23:15:49 +08:00 via iPhone
    equal to superview offset
    coloz
        17
    coloz  
       2021-01-15 23:17:45 +08:00
    为什么不选择跨平台的开发方式呢,搞两套 app 太累了
    vincentxue
        18
    vincentxue  
       2021-01-15 23:20:21 +08:00   ❤️ 1
    不是你没弄对,而是 iOS 的布局系统以及它的命令式 UI 开发体验跟 Android 比简直就是一坨屎,更别提和 Web 比了。

    iOS 的 Auto Layout 基本上等同于 Android 中的 ConstraintLayout,都是基于约束的布局系统。最大的不同点在于 iOS 中控件的尺寸基本上除了 UILabel / UITextView (相当于 Android 中的 TextView )、UIButton (相当于 Android 中的 *Button )、UIImageView (相当于 Android 中的 ImageView )之类有内容的控件还有一些系统固定尺寸的控件(如 UISegmentedControl 、UISearchBar 等)外,都是没有默认尺寸的。它们的尺寸是基于约束计算出来的。

    如果你在 Android 中用过 Google 的 FlexboxLayout,在 iOS 上推荐你用 https://github.com/layoutBox/FlexLayout 这个库。 它是基于 Facebook 开源的 Flexbox 布局引擎 Yoga 实现的,非常好用。百分百找回 Android 上的感觉。iOS 中有个叫 UIStackView 的控件实现了一些类似于 Flexbox 的控件组织方式,但是和真正的 Flexbox 还是差的太远了。

    但是说到底,iOS 最好还是要用 Auto Layout 来布局,因为这个才是最 Native 的布局方式。你用其他的布局方式之前,至少要搞投 Auto Layout 。

    你有兴趣的话可以加我的 tg 我给你解答,移动端是我的老本行,搞了十来年了,大家互相学习。
    oko
        19
    oko  
       2021-01-15 23:28:17 +08:00
    自动布局老心酸了,早已放弃,改用手动布局了,虽然烦了一点,但是手动控制每一个像素的感觉还是很美妙的
    Sixi
        20
    Sixi  
       2021-01-16 00:20:54 +08:00 via iPhone
    楼主的意思我懂,以前也是从这些坑里慢慢爬出来的。
    假如使用 SnapKit 为一个 View 添加约束之后,想要获取这个 View 的 宽高(实际上也就是 frame 值),但总是是 0 。这是因为你获取宽高的代码在你的 SnapKit 代码前执行,此时 View 并没有布局,默认的 frame 值均是 0,所以获取不到的。
    vincentxue
        21
    vincentxue  
       2021-01-16 03:44:06 +08:00   ❤️ 2
    我来补充一下楼上的回复。准确地说,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
        22
    Roccc  
       2021-01-16 10:04:36 +08:00
    百分比+SnapKit
    magic3584
        23
    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
        24
    magic3584  
       2021-01-16 10:15:57 +08:00
    楼上 code block 标记 OC 但实际写的 swift...
    expkzb
        25
    expkzb  
       2021-01-16 10:52:49 +08:00
    “我在网上搜了用 SnapKit 的方式可以不设置控件的高宽来使用,这个算是一种方式,但是用了这个方式之后,获取控件的高宽有时候为 0,好像是因为 snapKit 是异步的原因,太痛苦了,有时候就是需要高宽但是获取不到”

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

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

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

    自动布局很好用,一定要多练习,掌握它的思维方式。唯一值得诟病的是改起来比较头疼。
    loarland
        26
    loarland  
       2021-01-16 18:30:19 +08:00
    控件自身约束要完整,互相之间的优先级也要调整
    365473321
        27
    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
        28
    Freeego  
       2021-01-16 19:10:59 +08:00
    一般来说某个控件用了 AutoLayout 就不需要再获取 frame 了,这也是 AutoLayout 的目的,如果一定要获取的话就在约束之后调用父控件的 layoutIfNeeded()

    父控件根据子控件自适应宽高,一句话就是通过子控件的约束计算出父控件的大小,父控件本身不设置宽高限制。多练习几次以后就会熟练了,只要你在写完子控件的约束以后,能够从中推算出父控件的 contentSize 就行,就像解方程一样。
    Jackiehu
        29
    Jackiehu  
       2021-01-16 19:42:50 +08:00 via iPhone
    因为是异步绘制,需要在你获取最终 frame 前 layoutifneeded
    Jackiehu
        30
    Jackiehu  
       2021-01-16 19:54:42 +08:00 via iPhone
    autolayout 就是等 layoutsubviews 后才有的 frame,layoutifneeded 会让 view 立即绘制,所以在之后可以拿到大小宽高
    365473321
        31
    365473321  
       2021-01-16 20:00:45 +08:00
    @365473321 子视图调用这两个方法可以确定子视图的 size,父视图调用这两个方法可以确定子视图的 frame
    xhpan10
        32
    xhpan10  
       2021-01-16 21:57:34 +08:00
    通过约束条件还有自定义 View 的 content size 来做适配啊
    Xcution
        33
    Xcution  
    OP
       2021-01-18 17:24:49 +08:00
    @Freeego @expkzb 我就是遇到这种问题,比如说我用 UICollectionView,显示的网格状的,有时候有 5 排数据,有时候有 6 排数据,如果我不设置 UICollectionView 的高度内容就显示不出来,但是设置我又不知道该设置多少,还有种就是知道每个 cell 的高度来算高度进行设置,这种也行,但是万一我 cell 的高度也是动态的啦,要计算 cell 的高度就要显示出来才能计算,不设置 UICollectionView 的高度有显示不出来,所以就必须要设置 UICollectionView 的高度,但是因为是动态的我又不知道该设置多少
    Freeego
        34
    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
        35
    kfchyc  
       2021-01-29 14:29:24 +08:00
    在一个 controller 的不同生命周期函数中,view 的一些视觉属性可能错误或为 0,具体情况取决于你写在哪和你的需要如何
    CodingIran
        36
    CodingIran  
       2021-02-01 13:09:19 +08:00
    @vincentxue 害 何必折腾呢 frame 布局不香么 用上面某层的回复“手动精准控制每一个像素的位置”不香么
    vincentxue
        37
    vincentxue  
       2021-02-02 02:44:25 +08:00
    @CodingIran frame 布局香在哪里...
    CodingIran
        38
    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
        39
    iXInbo  
       2021-02-07 00:47:28 +08:00
    snapKit 基于苹果的 AutoLayout,会根据屏幕自适应
    使用 frame 就是先给定宽度和高度的。
    如果你需要 frame 和 AutoLayout 混用,可以试试 layoutIfNeeded 先于 frame 调用。
    jiahy
        40
    jiahy  
       2021-03-25 11:14:07 +08:00
    自适应布局的话可以试试 UIStackView 嵌套布局,Autolayout 自适应大小适用控件重写了 IntrinsicContentSize,自动布局的时候会根据这个来自动设置约束,本质上还是要自己计算。不过一些常用的系统控件都实现了自动计算内容大小,关键约束设置正确就不用管了,其实设置一个完美自适应的约束还是比较烦的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2761 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:11 · PVG 15:11 · LAX 23:11 · JFK 02:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.