SwiftUI:将 Circle "fit" 设为 Text 元素
SwiftUI: Make Circle "fit" the Text element
我有以下代码:
import SwiftUI
struct EntryHeaderIconView: View {
private let backgroundSize: Double = 88
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
ZStack(alignment: .center) {
Circle()
// .frame(width: backgroundSize,
// height: backgroundSize)
// Uncommenting this fixes the issue, but the text now clips
.foregroundColor(color)
icon
}
}
@ViewBuilder
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
struct ContentView: View {
var body: some View {
VStack {
EntryHeaderIconView(color: .red,
initials: "RT")
EntryHeaderIconView(color: .green,
initials: "LV")
EntryHeaderIconView(color: .red,
initials: "中国")
Spacer()
}
}
}
我的目标是让 Circle
元素完全适合 Text
元素。然而,它要么在 VStack
中继续增长,以便它占据尽可能多的 space 并且看起来像这样(尺寸线被注释掉):
或者,如果我将固定大小设置为 Circle
,内容将被剪裁:
var body: some View {
ZStack(alignment: .center) {
Circle()
.frame(width: backgroundSize,
height: backgroundSize)
// Uncommenting this fixes the issue, but the text now clips
.foregroundColor(color)
icon
}
}
我的目标是使 Circle
大小依赖于 Text
大小,以便它一起增长(或缩小)。
使用 AutoLayout 很容易实现,如何使用 SwiftUI 实现同样的效果?
A ZStack
占据其子视图所需的 space 。您没有为 Circle
提供明确的 frame
,那么 SwiftUI 怎么知道 Circle
的大小应该与 icon
的大小匹配?
相反,在将一些 padding
应用到 icon
之后,您应该将 Circle
作为 background
添加到您的 icon
。
struct EntryHeaderIconView: View {
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
icon
.padding(.all, 30)
.background(background)
}
private var background: some View {
Circle()
.foregroundColor(color)
}
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
struct EntryHeaderIconView_Preview: PreviewProvider {
static var previews: some View {
VStack {
EntryHeaderIconView(color: .red,
initials: "RT")
EntryHeaderIconView(color: .green,
initials: "LV")
EntryHeaderIconView(color: .red,
initials: "中国")
}
}
}
您需要对文本应用填充,然后以圆形显示背景。
以下是实现该目标的方法:
struct Example: View {
var body: some View {
VStack {
Text("1")
.font(.footnote)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
Text("2")
.font(.headline)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
Text("3")
.font(.largeTitle)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
}
}
}
我要 post 这是因为它与其他两个答案不同,后者更简单并且有效。这在处理不同尺寸时更加灵活,但仍然使它们看起来一致。这使用 PreferenceKey 来读取首字母的大小,并将圆限制为一定大小。
struct EntryHeaderIconView: View {
@State var textViewSize = CGFloat.zero
// This gives a consistent additional size of the circle around the intitials
let circleSizeMultiplier = 1.5
// To give it a minimum size, just have the size introduced here
let minimumSize: CGfloat
var backgroundSize: CGFloat {
min(textViewSize * circleSizeMultiplier, minimumSize)
}
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
ZStack(alignment: .center) {
Circle()
// The frame is sized at the circleSizeMultiplier times the
// largest dimension of the initials.
.frame(width: backgroundSize
height: backgroundSize)
.foregroundColor(color)
icon
// This reads the size of the initials
.background(GeometryReader { geometry in
Color.clear.preference(
key: SizePreferenceKey.self,
value: geometry.size
)
})
}
// this sets backgroundSize to be the max value of the width or height
.onPreferenceChange(SizePreferenceKey.self) {
textViewSize = max([=10=].width, [=10=].height)
}
}
@ViewBuilder
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
// This is the actual preferenceKey that makes it work.
fileprivate struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}
编辑:
除非图标是 SF 符号,否则必须以不同方式处理。但是,我更新了代码以添加 minimumSize
常量并将 backgroundSize
更改为计算变量。此视图未设置为处理图像,但您只需要确定要如何约束图像,或执行类似 .
的操作
我有以下代码:
import SwiftUI
struct EntryHeaderIconView: View {
private let backgroundSize: Double = 88
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
ZStack(alignment: .center) {
Circle()
// .frame(width: backgroundSize,
// height: backgroundSize)
// Uncommenting this fixes the issue, but the text now clips
.foregroundColor(color)
icon
}
}
@ViewBuilder
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
struct ContentView: View {
var body: some View {
VStack {
EntryHeaderIconView(color: .red,
initials: "RT")
EntryHeaderIconView(color: .green,
initials: "LV")
EntryHeaderIconView(color: .red,
initials: "中国")
Spacer()
}
}
}
我的目标是让 Circle
元素完全适合 Text
元素。然而,它要么在 VStack
中继续增长,以便它占据尽可能多的 space 并且看起来像这样(尺寸线被注释掉):
或者,如果我将固定大小设置为 Circle
,内容将被剪裁:
var body: some View {
ZStack(alignment: .center) {
Circle()
.frame(width: backgroundSize,
height: backgroundSize)
// Uncommenting this fixes the issue, but the text now clips
.foregroundColor(color)
icon
}
}
我的目标是使 Circle
大小依赖于 Text
大小,以便它一起增长(或缩小)。
使用 AutoLayout 很容易实现,如何使用 SwiftUI 实现同样的效果?
A ZStack
占据其子视图所需的 space 。您没有为 Circle
提供明确的 frame
,那么 SwiftUI 怎么知道 Circle
的大小应该与 icon
的大小匹配?
相反,在将一些 padding
应用到 icon
之后,您应该将 Circle
作为 background
添加到您的 icon
。
struct EntryHeaderIconView: View {
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
icon
.padding(.all, 30)
.background(background)
}
private var background: some View {
Circle()
.foregroundColor(color)
}
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
struct EntryHeaderIconView_Preview: PreviewProvider {
static var previews: some View {
VStack {
EntryHeaderIconView(color: .red,
initials: "RT")
EntryHeaderIconView(color: .green,
initials: "LV")
EntryHeaderIconView(color: .red,
initials: "中国")
}
}
}
您需要对文本应用填充,然后以圆形显示背景。
以下是实现该目标的方法:
struct Example: View {
var body: some View {
VStack {
Text("1")
.font(.footnote)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
Text("2")
.font(.headline)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
Text("3")
.font(.largeTitle)
.foregroundColor(.white)
.padding(5)
.background(.red)
.clipShape(Circle())
}
}
}
我要 post 这是因为它与其他两个答案不同,后者更简单并且有效。这在处理不同尺寸时更加灵活,但仍然使它们看起来一致。这使用 PreferenceKey 来读取首字母的大小,并将圆限制为一定大小。
struct EntryHeaderIconView: View {
@State var textViewSize = CGFloat.zero
// This gives a consistent additional size of the circle around the intitials
let circleSizeMultiplier = 1.5
// To give it a minimum size, just have the size introduced here
let minimumSize: CGfloat
var backgroundSize: CGFloat {
min(textViewSize * circleSizeMultiplier, minimumSize)
}
private let color: Color
private let initials: String
init(color: Color,
initials: String = "") {
self.color = color
self.initials = initials
}
var body: some View {
ZStack(alignment: .center) {
Circle()
// The frame is sized at the circleSizeMultiplier times the
// largest dimension of the initials.
.frame(width: backgroundSize
height: backgroundSize)
.foregroundColor(color)
icon
// This reads the size of the initials
.background(GeometryReader { geometry in
Color.clear.preference(
key: SizePreferenceKey.self,
value: geometry.size
)
})
}
// this sets backgroundSize to be the max value of the width or height
.onPreferenceChange(SizePreferenceKey.self) {
textViewSize = max([=10=].width, [=10=].height)
}
}
@ViewBuilder
private var icon: some View {
Text(verbatim: initials)
.font(.system(size: 48, weight: .bold, design: .rounded))
.foregroundColor(.white)
.accessibilityIdentifier("entry_header_initials")
}
}
// This is the actual preferenceKey that makes it work.
fileprivate struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}
编辑:
除非图标是 SF 符号,否则必须以不同方式处理。但是,我更新了代码以添加 minimumSize
常量并将 backgroundSize
更改为计算变量。此视图未设置为处理图像,但您只需要确定要如何约束图像,或执行类似