如何缩放 SwiftUI 图像,包括它的覆盖层?
How to scale a SwiftUI Image including its overlay?
鉴于此视图的图像具有叠加层:
struct TestView: View {
var body: some View {
Image("image")
.overlay {
Image("logo")
.position(x: 20, y: 130) // I want to freely position the overlay image
}
}
}
图像的大小无关紧要。我的示例是 150×150 像素(“图像”)和 20×20 像素(徽标)。
我怎样才能将它放大到它的边界(比如 superviews 框架),包括 叠加层?
一些必备条件:
我需要自由定位叠加图片,所以我不能将overlay
的alignment
参数与徽标上的填充。
我还需要定位叠加图像绝对,而不是相对。所以我不能使用 GeometryReader
(或 alignment
)。
我需要视图在不同场景(不同设备、不同视图层次结构)中可重用。这意味着我不知道比例因子,所以我不能使用scaleEffect
。
我试过了:
A) .aspectRatio(contentMode: .fit)
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.position(x: 20, y: 130)
}
.aspectRatio(contentMode: .fit)
}
}
B) .scaledToFit()
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.position(x: 20, y: 130)
}
.scaledToFit()
}
}
C) 使徽标可调整大小
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.resizable()
.position(x: 20, y: 130)
}
.scaledToFit() // or .aspectRatio(contentMode: .fit)
}
}
A 和 B 看起来像这样:
C 看起来像这样:
两者都给了我一个与原始徽标不同的相对位置(参见链接的屏幕截图)。相对大小也不同(现在太小了)。
包裹在堆栈中并改为缩放此堆栈也没有帮助。
[更新] 接受的答案
显然,在 SwiftUI 和 UIKit 中没有真正的视图层次结构是有代价的。缩放层次结构不应该那么复杂恕我直言。相反,我仍然希望 .scaledToFit
(如果在末尾添加)扩展它之前的所有内容(参见 A、B)和 C))。
改编(减去contentSize
和alignment: .topLeading
):
struct ContentView: View {
var body: some View {
Image("image")
.resizable()
.scaledToFit()
.overlay {
GeometryReader { geometry in
let imageSize = CGSize(width: 150, height: 150)
let logoSize = CGSize(width: 20, height: 20)
let logoPosition = CGPoint(x: 20, y: 130)
Image("logo")
.resizable()
.position(
x: logoPosition.x / imageSize.width * geometry.size.width,
y: logoPosition.y / imageSize.height * geometry.size.height
)
.frame(
width: logoSize.width / imageSize.width * geometry.size.width,
height: logoSize.height / imageSize.height * geometry.size.height
)
}
}
}
}
问题是徽标的绝对定位。我看到两种方式:
使用 .scaleEffect 缩放整个结果图像。
请注意,这是底层图像的渲染缩放,它不会以新尺寸重新绘制,因此可能会变得模糊。
Image("dog")
.overlay {
Image(systemName: "circle.hexagongrid.circle.fill")
.foregroundColor(.pink)
.position(x: 20, y: 130)
}
.scaleEffect(2.0)
或者 – 以及我的偏好
2. Logo相对于左下角的位置
ZStack(alignment: .bottomLeading) {
Image("dog")
.resizable()
.scaledToFit()
Image(systemName: "circle.hexagongrid.circle.fill")
.resizable()
.foregroundColor(.pink)
.frame(width: 70, height: 70)
.padding()
}
然后像这样使用 GeometryReader:
struct ContentView: View {
let imageSize = CGSize(width: 150, height: 150) // Size of orig. image
let logoPos = CGSize(width: 10, height: 120) // Position of Logo in relation to orig. image
let logoSize = CGSize(width: 20, height: 20) // Size of logo in relation to orig. image
@State private var contentSize = CGSize.zero
var body: some View {
Image("dog")
.resizable()
.scaledToFit()
.overlay(
GeometryReader { geo in
Color.clear.onAppear {
contentSize = geo.size
}
}
)
.overlay(
Image(systemName: "circle.hexagongrid.circle.fill")
.resizable()
.foregroundColor(.pink)
.offset(x: logoPos.width / imageSize.width * contentSize.width,
y: logoPos.height / imageSize.height * contentSize.height)
.frame(width: logoSize.width / imageSize.width * contentSize.width,
height: logoSize.height / imageSize.height * contentSize.height)
, alignment: .topLeading)
}
}
鉴于此视图的图像具有叠加层:
struct TestView: View {
var body: some View {
Image("image")
.overlay {
Image("logo")
.position(x: 20, y: 130) // I want to freely position the overlay image
}
}
}
图像的大小无关紧要。我的示例是 150×150 像素(“图像”)和 20×20 像素(徽标)。
我怎样才能将它放大到它的边界(比如 superviews 框架),包括 叠加层?
一些必备条件:
我需要自由定位叠加图片,所以我不能将
overlay
的alignment
参数与徽标上的填充。我还需要定位叠加图像绝对,而不是相对。所以我不能使用
GeometryReader
(或alignment
)。我需要视图在不同场景(不同设备、不同视图层次结构)中可重用。这意味着我不知道比例因子,所以我不能使用
scaleEffect
。
我试过了:
A) .aspectRatio(contentMode: .fit)
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.position(x: 20, y: 130)
}
.aspectRatio(contentMode: .fit)
}
}
B) .scaledToFit()
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.position(x: 20, y: 130)
}
.scaledToFit()
}
}
C) 使徽标可调整大小
struct TestView: View {
var body: some View {
Image("image")
.resizable()
.overlay {
Image("logo")
.resizable()
.position(x: 20, y: 130)
}
.scaledToFit() // or .aspectRatio(contentMode: .fit)
}
}
A 和 B 看起来像这样:
C 看起来像这样:
两者都给了我一个与原始徽标不同的相对位置(参见链接的屏幕截图)。相对大小也不同(现在太小了)。
包裹在堆栈中并改为缩放此堆栈也没有帮助。
[更新] 接受的答案
显然,在 SwiftUI 和 UIKit 中没有真正的视图层次结构是有代价的。缩放层次结构不应该那么复杂恕我直言。相反,我仍然希望 .scaledToFit
(如果在末尾添加)扩展它之前的所有内容(参见 A、B)和 C))。
改编contentSize
和alignment: .topLeading
):
struct ContentView: View {
var body: some View {
Image("image")
.resizable()
.scaledToFit()
.overlay {
GeometryReader { geometry in
let imageSize = CGSize(width: 150, height: 150)
let logoSize = CGSize(width: 20, height: 20)
let logoPosition = CGPoint(x: 20, y: 130)
Image("logo")
.resizable()
.position(
x: logoPosition.x / imageSize.width * geometry.size.width,
y: logoPosition.y / imageSize.height * geometry.size.height
)
.frame(
width: logoSize.width / imageSize.width * geometry.size.width,
height: logoSize.height / imageSize.height * geometry.size.height
)
}
}
}
}
问题是徽标的绝对定位。我看到两种方式:
使用 .scaleEffect 缩放整个结果图像。
请注意,这是底层图像的渲染缩放,它不会以新尺寸重新绘制,因此可能会变得模糊。Image("dog") .overlay { Image(systemName: "circle.hexagongrid.circle.fill") .foregroundColor(.pink) .position(x: 20, y: 130) } .scaleEffect(2.0)
或者 – 以及我的偏好
2. Logo相对于左下角的位置
ZStack(alignment: .bottomLeading) {
Image("dog")
.resizable()
.scaledToFit()
Image(systemName: "circle.hexagongrid.circle.fill")
.resizable()
.foregroundColor(.pink)
.frame(width: 70, height: 70)
.padding()
}
然后像这样使用 GeometryReader:
struct ContentView: View {
let imageSize = CGSize(width: 150, height: 150) // Size of orig. image
let logoPos = CGSize(width: 10, height: 120) // Position of Logo in relation to orig. image
let logoSize = CGSize(width: 20, height: 20) // Size of logo in relation to orig. image
@State private var contentSize = CGSize.zero
var body: some View {
Image("dog")
.resizable()
.scaledToFit()
.overlay(
GeometryReader { geo in
Color.clear.onAppear {
contentSize = geo.size
}
}
)
.overlay(
Image(systemName: "circle.hexagongrid.circle.fill")
.resizable()
.foregroundColor(.pink)
.offset(x: logoPos.width / imageSize.width * contentSize.width,
y: logoPos.height / imageSize.height * contentSize.height)
.frame(width: logoSize.width / imageSize.width * contentSize.width,
height: logoSize.height / imageSize.height * contentSize.height)
, alignment: .topLeading)
}
}