xcode 自动将按钮的内容形状与其图像的不透明部分对齐?

jucafojl  于 8个月前  发布在  其他
关注(0)|答案(2)|浏览(70)

我已经用SwiftUI设置了一个Xcode项目。我正在处理的屏幕有一个滚动视图:

背景看起来很奇怪,因为我有几个全屏图像的透明背景,这是为了在顶部分层:

通过使图像全屏显示,如果所有图像和背景都设置为scaledToFill,它们将始终与背景对齐。问题在于这些图像是用来充当 * 按钮 * 的。通过将这些图像作为标签添加到按钮,我可以使它们正常工作而不会看起来奇怪。当然,这些图像是全屏的,所以按钮的内容区域也是全屏的。我可以通过使用路径制作自定义形状并将其用于内容形状来解决这个问题:

Button {
    runFunction()
} label: {
    Image(imageName)
        .resizable()
        .scaledToFill()
}
    .contentShape(tappableArea)

字符串
现在我们来看看我写这篇文章的真正原因。因为图像是全屏的,每个图像的不透明部分从来没有居中,总是不同的,我找不到一种方法来使每个图像的内容形状正确对齐。我可以让它正确对齐,如果我给每个形状添加偏移和缩放,但是这些硬编码的改变只对一个设备有效。如果我在另一个设备上运行这个应用程序,事情就会再次失去一致性。
有没有一种方法可以自动将每个按钮的内容形状与图像的不透明部分对齐,而不管屏幕尺寸如何?也许我可以将一个公式应用于每个形状的比例和偏移量?请记住,因为这一切都被包裹在滚动视图中,视图将始终保持相同的比例。

nzk0hqpo

nzk0hqpo1#

您可以将按钮(或可缩放区域)移动到覆盖层,并使用GeometryReader来测量底层图像的大小。然后您可以使用相对大小来定位和调整可缩放区域的大小。
就像这样:

Image(imageName)
    .resizable()
    .scaledToFill()
    .overlay {
        GeometryReader { proxy in
            let w = proxy.size.width
            let h = proxy.size.height
            Rectangle()
                .opacity(0.001)
                .frame(width: w * 0.2, height: h * 0.1)
                // .background(Rectangle().stroke() // for debugging
                .padding(.leading, w * 0.27)
                .padding(.top, h * 0.31)
                .onTapGesture {
                    runFunction()
                }
        }
    }

字符串
你可以在同一个覆盖中有多个可显示区域(在这种情况下,你可以使用ZStack来包含它们)。你也可以使用不同的形状来定义一个可显示区域。
我认为GeometryReader x将给予您完整图像的大小,而不仅仅是可见部分。这很方便,因为否则您可能必须从GeometryReader x提供的大小和(已知的)图像宽高比计算实际图像大小。

wmtdaxz3

wmtdaxz32#

折腾了几天后,我终于想出了一个解决方案!对于我的每个按钮,我都做了以下操作:

1.使用几何阅读器在滚动视图中添加背景叠加

ScrollView(.horizontal) {
    Image("Background Image Name")
        .resizable()
        .scaledToFill()
        .overlay {
            GeometryReader { proxy in

            }
        }
}

字符串
目视检查结果:
x1c 0d1x的数据

2.添加按钮全屏图片:

ScrollView(.horizontal) {
    Image("Background Image Name")
        .resizable()
        .scaledToFill()
        .overlay {
            GeometryReader { proxy in
                Image("Image Name")
                    .resizable()
                    .scaledToFill()
            }
        }
}


目视检查结果:


**3.在图像后面添加一个矩形,调整其大小以与图像的可见部分对齐。**我使用填充和几何阅读器来测量此矩形相对于背景图像的大小。重要的是,您只能在输入硬数字时使用乘法(*)或除法(/)。否则,您最终得到的值将不会与屏幕大小成比例。

ScrollView(.horizontal) {
    Image("Background Image Name")
        .resizable()
        .scaledToFill()
        .overlay {
            GeometryReader { proxy in
                let w = proxy.size.width
                let h = proxy.size.height

                Rectangle()
                    .fill(.green)
                    .padding(.init(top: h / 1.8, leading: w / 1.42, bottom: h / 16, trailing: w / 8.70))

                Image("Image Name")
                    .resizable()
                    .scaledToFill()
            }
        }
}


目视检查结果:


**4.如果按钮的内容形状是自定义形状,则将其添加到顶部,使用矩形的相同尺寸将其对齐。**我在形状上添加了一个笔划,以便可以看到它,但添加填充颜色也可以。

ScrollView(.horizontal) {
    Image("Background Image Name")
        .resizable()
        .scaledToFill()
        .overlay {
            GeometryReader { proxy in
                let w = proxy.size.width
                let h = proxy.size.height

                let topOffset = h / 1.8
                let leadingOffest = w / 1.42
                let bottomOffset = h / 16
                let trailingOffset = w / 8.7

                Rectangle()
                    .fill(.green)
                    .padding(.init(top: topOffset, leading: leadingOffest, bottom: bottomOffset, trailing: trailingOffset))

                Image("Image Name")
                    .resizable()
                    .scaledToFill()

                CustomContentShape()
                    .stroke(.blue, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
                    .padding(EdgeInsets(top: topOffset, leading: leadingOffest, bottom: bottomOffset, trailing: trailingOffset))
            }
        }
}


目视检查结果:



你会发现,这个形状将正确缩放,无论屏幕大小,就像图像一样!我还没有测试横向模式,因为我的应用程序不支持它,但我确实测试了两种不同类型的iPad和iPhone在我的画布。
现在剩下的就是:

5.将内容形状添加到按钮中。将填充应用到形状中,然后尝试将其用作按钮的内容形状会带来许多问题,但您可以解决这个问题。在设置内容形状后直接将填充应用到按钮中,并将相同的填充应用到图像中,但具有负值,然后将图像移动到矩形的剪切叠加。现在,如果你把矩形移动到按钮的标签上,你的按钮就工作了:

ScrollView(.horizontal) {
    Image("Background Image Name")
        .resizable()
        .scaledToFill()
        .overlay {
            GeometryReader { proxy in
                let w = proxy.size.width
                let h = proxy.size.height

                let topOffset = h / 1.8
                let leadingOffest = w / 1.42
                let bottomOffset = h / 16
                let trailingOffset = w / 8.7
                            
                Button {
                    runFunction()
                } label: {
                   Rectangle()
                       .fill(.clear)
                       .overlay {              
                           Image("Image Name")
                               .resizable()
                               .scaledToFill()
                               .clipped()
                               .padding(EdgeInsets(top: -topOffset, leading: -leadingOffest, bottom: -bottomOffset, trailing: -trailingOffset))
                       }
                }
                .contentShape(button.tappableArea)
                .padding(EdgeInsets(top: topOffset, leading: leadingOffest, bottom: bottomOffset, trailing: trailingOffset))
            }
        }
}


目视检查结果:

需要注意的是,在设置自定义形状时,我在函数中使用了路径,如下所示:

struct CustomContentShape: Shape {
    
    func path(in rect: CGRect) -> Path {
        var pencil = Path()
    
        pencil.move(to: CGPoint(x: rect.minX + (rect.maxX / 7), y: rect.maxY))
        pencil.addLine(to: CGPoint(x: rect.minX, y: rect.midY + (rect.maxX / 4)))
        pencil.addLine(to: CGPoint(x: rect.midX - (rect.maxX / 5), y: rect.minY + (rect.maxX / 3)))
        pencil.addLine(to: CGPoint(x: rect.minX + (rect.maxX / 8), y: rect.minY + (rect.maxX / 7)))
        pencil.addLine(to: CGPoint(x: rect.minX + (rect.maxX / 4.7), y: rect.minY))
        pencil.addLine(to: CGPoint(x: rect.midX, y: rect.minY + (rect.maxX / 8)))
        pencil.addLine(to: CGPoint(x: rect.midX, y: rect.midY + (rect.maxX / 5)))
        pencil.addLine(to: CGPoint(x: rect.maxX, y: rect.midY + (rect.maxX / 8)))
        pencil.addLine(to: CGPoint(x: rect.maxX, y: rect.midY + (rect.maxX / 2)))
        pencil.closeSubpath()
    
        return pencil
    }
}

**所有使用硬编码数字的计算都必须是乘法或除法。通过使用形状矩形“边界框”的最小值,中间值和最大值进行这些计算,您可以确保您的形状与其图像保持比例。**如果您不知道这意味着什么,请查看在Swift中使用路径来创建自定义形状。我使用了以下指南:https://medium.com/@amosgyamfi/using-calculated-geometric-properties-how-to-draw-complex-shapes-in-swiftui-7c6beccdbedf虽然需要注意的是,他们让你在教程中添加的var** controlPoint: Double实际上并没有用于任何东西,我只是把它从代码中去掉了。

最后,在滚动视图中设置它意味着每次你在代码中更改一个值时,视图都会重新加载,这会将滚动视图捕捉回其默认位置。如果你试图在屏幕外编辑一些东西,这会变得很旧。为了解决这个问题,我通常使用这个方法:SwiftUI如何防止视图重新加载整个身体然而,这似乎并不总是工作时,由于某种原因,我不能理解复杂的视图,所以我使用的另一个技巧是在body中的每个视图上使用.scaleEffect(x: -1, y: 1)来翻转它。这使得形状的min,mid和max值现在被翻转,这会让人感到困惑,但是当你在应用中实现它们时,输入的值仍然是正确的。
祝各位程序员好运!

相关问题