美文网首页
swiftui Text控制多行显示fixedSize和Pref

swiftui Text控制多行显示fixedSize和Pref

作者: 咚咚嗒大人 | 来源:发表于2024-09-14 13:26 被阅读0次

需求:
body要求设置一个最小高度minHeight,body内的content进行垂直排列VStack。且当content内容较少时,置顶展示。
问题1:
VStack中无法设置alignment: .top
问题2:
使用Spacer()会导致垂直距离,直接被撑开。无法满足minHeight的要求。


处理方法:
所以为了使用Spacer(),就要直接计算出content的高度,将body的height设置为:max(content高度,minHeight)

一:.preference(key:defaultValue:transform:)
用途:
  • 用于定义一种偏好(preference),可以在视图层次结构中传递特定类型的值。
  • 通过在视图树中的不同位置设置和读取偏好值,可以实现跨视图的信息传递和响应。

其实就是在你想在视图树中哪个位置获取height or size 等信息,用这个方法把信息发送出去,类似与对应UIKit的postNotification。

二:.onPreferenceChange(key:perform:)
用途:
  • 用于响应由.preference或.anchorPreference定义的偏好变化。
  • 当偏好值发生变化时,.onPreferenceChange中的代码块将被执行,允许你根据偏好的变化进行相应的操作。

其实就是用来监听当value发生变化,这个方法会被响应,类似与UIKit中的addNotification。

所以我们有了下面的代码

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

struct ContentView: View {
    @State private var vstackHeight: CGFloat = 0
        VStack {
            VStack(spacing: .zero) {
                VStack(spacing: .zero) {
                    ForEach(0..<5) { tag in
                        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                            .foregroundStyle(Color.gray)
                            .font(.system(size: 14))
                            .background{Color.yellow}
                            .padding(Constants.spacing10)
                            .frame(minHeight: Constants.spacing20)
                            .frame(maxWidth:.infinity, alignment:.leading)
                            .background(GeometryReader { geo in
                                Color.red.preference(key: HeightPreferenceKey.self, value: geo.size.height)
                            })
                    }
                }
                Spacer()
            }
            .background {
                Color.purple
            }
            .frame(height: max(300, vstackHeight))
            .onPreferenceChange(HeightPreferenceKey.self) { height in
                vstackHeight = height
            }
            Text("---jlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlife---")
                .foregroundStyle(Color.gray)
                .font(.system(size: 18))
                .padding(Constants.spacing10)
                .background(Color.blue)
            Spacer()
        }
        .background {
            Color.black
        }
    }
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.28.48.png

当内容过多时,出现文字显示不全的问题

于是给Text()添加fixedSize属性

.fixedSize(horizontal: false, vertical: true)

即:

ForEach(0..<5) { tag in
    Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
        .foregroundStyle(Color.gray)
        .font(.system(size: 14))
        .background{Color.yellow}
        .padding(Constants.spacing10)
        .frame(minHeight: Constants.spacing20)
        .frame(maxWidth:.infinity, alignment:.leading)
        .fixedSize(horizontal: false, vertical: true) // 添加这个属性
        .background(GeometryReader { geo in
            Color.red.preference(key: HeightPreferenceKey.self, value: geo.size.height)
        })
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.29.26.png

添加.fixedSize()属性后,内容虽然显示完整了,但又能看到,外层的VStack()并没有撑开,随意底部蓝色区域覆盖了红色的区域。

这是因为.preference()取值出现问题,继续修改:

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
//        value = nextValue()
        value += nextValue()
    }
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.29.42.png
完整代码为:
import SwiftUI
struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
//        value = nextValue()
        value += nextValue()
    }
}

struct ContentView: View {
    @State private var vstackHeight: CGFloat = 0
    var body: some View {
        VStack(spacing: .zero) {
            VStack(spacing: .zero) {
                VStack(spacing: .zero) {
                    ForEach(0..<3) { tag in
                        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                            .foregroundStyle(Color.gray)
                            .font(.system(size: 14))
                            .background{Color.yellow}
                            .padding(Constants.spacing10)
                            .frame(minHeight: Constants.spacing20)
                            .frame(maxWidth:.infinity, alignment:.leading)
                            .fixedSize(horizontal: false, vertical: true) // 添加这个属性
                            .background(GeometryReader { geo in
                                Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
                            })
                    }
                }
                if vstackHeight < 300  { Spacer() }
            }
            .background {
                Color.purple
            }
            .frame(height: max(300, vstackHeight))
            .onPreferenceChange(HeightPreferenceKey.self) { height in
                vstackHeight = height
            }
            Text("---jlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlife---")
                .foregroundStyle(Color.gray)
                .font(.system(size: 18))
                .padding(Constants.spacing10)
                .background(Color.blue)
            Spacer(minLength: 0)
        }
        .background {
            Color.black
        }
    }
}

思考如下代码中将:
Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
直接放置在外层VStack中,
发现并不好使,具体原因还不知道,难道.preference()不能放置在外层VStack()中?
原谅楼主也是一个小白而已。。。有知道的请下面评论,非常感谢~

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
//        value += nextValue()
    }
}
VStack(spacing: .zero) {
    ForEach(0..<3) { tag in
        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
            .foregroundStyle(Color.gray)
            .font(.system(size: 14))
            .background{Color.yellow}
            .padding(Constants.spacing10)
            .frame(minHeight: Constants.spacing20)
            .frame(maxWidth:.infinity, alignment:.leading)
            .fixedSize(horizontal: false, vertical: true) // 添加这个属性
//                            .background(GeometryReader { geo in
//                                Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
//                            })
    }
}
.background(GeometryReader { geo in
    Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
})

New: 上面之所以会有问题,是因为对frame的height获取后,又对superview的height设置,导致重新约束布局,所以会引起问题。

法一: 通过获取到的height,当height < minHeight时:对内部整体的view做一个y轴的offset。
.offset(x: 0, y: vstackHeight < 300 ? -(300 - vstackHeight) / 2.0 : 0)
法二:就是通过ZStack的alignment,进行约束控制

其中VStack{}.frame(minHeight: 300)是为了撑开ZStack,这样就能使content安装.top对齐

ZStack(alignment: .top) {
    VStack{}.frame(minHeight: 300)
    VStack(spacing: .zero) {
        ForEach(0..<1) { tag in
            Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                .foregroundStyle(Color.gray)
                .font(.system(size: 14))
                .background{Color.yellow}
                .padding(Constants.spacing10)
                .frame(minHeight: Constants.spacing20)
        }
    }
}
注意不能直接设置在下面这里,因为这样VStack又被撑开了,内部还是会按VStack居中布局。。。
ZStack(alignment: .top) {
    VStack(spacing: .zero) {
        ForEach(0..<1) { tag in
            Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                .foregroundStyle(Color.gray)
                .font(.system(size: 14))
                .background{Color.yellow}
                .padding(Constants.spacing10)
                .frame(minHeight: Constants.spacing20)
        }
    }
    .frame(minHeight: 300)//不能设置在这里
}

相关文章

网友评论

      本文标题:swiftui Text控制多行显示fixedSize和Pref

      本文链接:https://www.haomeiwen.com/subject/bpankjtx.html