我正在尝试让按钮看起来像这样。
也就是说,按钮会随着点击而改变其显示状态,有点类似于TabBar。当Bottom被激活时,会出现渐变背景,Bottom上的边框消失,下方显示对应的View。两个Button都是这样完成切换的。
本来打算通过Button和View分离的方式来实现,但是遇到了困难。
当我用SwiftUI实现它时,我在背景中添加了一个比前面大的矩形作为特定的边和圆角边框。结果和原来的差不多,只是圆角有点奇怪。
实施情况如下:
这两个Button放在同一个Hstack中。起初,我设置的是
HStack(spacing: 0) {}
消除了按钮之间预设的间距,但是后台设置的矩形似乎不被认为是一个实体,所以我将间距设置为我想要的边框宽度,即3。但我怀疑这是最好的方法。
另外,自定义圆角的方法我参考https://www.devtechie.com/community/public/posts/151937-round-specific-corners-in-swiftui。
我已经在故事板中实现了它们,但由于 SwiftUI 的操作方式不同,我很难复制它们。我想知道是否有办法改善稍厚的圆角。以及如何让底部边框实现的看起来像和 Button 一体成型的,目前有没有更好的办法可以改进。程序代码如下:
HStack(spacing: 0) {
Button(action: {
}){
Text("行程簡介")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color("DesignMiddleMutedOrange"))
.multilineTextAlignment(.center)
.padding(.horizontal, 34.0)
}
.DetailButtonisOnActive()
Button(action: {
}){
Text("Q&A")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color("DesignMiddleMutedOrange"))
.multilineTextAlignment(.center)
.padding(.horizontal, 34.0)
}
.QAButtonisOffActive()
Spacer()
}
两种按钮样式:
extension Button {
func DetailButtonisOnActive() -> some View {
self
.frame(width: 150, height: 52, alignment: .leading)
.background(
LinearGradient(colors: [Color("#FFFAE6"), Color("DesignLightYellow")],
startPoint: .top,
endPoint: .bottom)
)
.roundedCorner(12, corners: .topRight)
.background(
Rectangle()
.roundedCorner(12, corners: .topRight)
.frame(width: 153, height: 55, alignment: .leading)
.padding(.bottom, 3)
.foregroundColor(Color("DesignLightMutedOrange"))
)
}
func DetailButtonisOffActive() -> some View {
self
.frame(width: 150, height: 52, alignment: .leading)
.background(
Color("DesignLightYellow")
)
.roundedCorner(12, corners: .topRight)
.border(.bottom, width: 3, color: Color("DesignLightMutedOrange"))
.background(
Rectangle()
.roundedCorner(12, corners: .topRight)
.frame(width: 153, height: 55, alignment: .leading)
.padding(.bottom, 3)
.foregroundColor(Color("DesignLightMutedOrange"))
)
}
func QAButtonisOnActive() -> some View {
self
.frame(width: 150, height: 52, alignment: .leading)
.background(
LinearGradient(colors: [Color("#FFFAE6"), Color("DesignLightYellow")],
startPoint: .top,
endPoint: .bottom)
)
.roundedCorner(12, corners: [.topRight, .topLeft])
//.padding(.leading, -1.5)
.background(
Rectangle()
.roundedCorner(12, corners: [.topRight, .topLeft])
.frame(width: 156, height: 55, alignment: .leading)
.padding(.bottom, 3)
.foregroundColor(Color("DesignLightMutedOrange"))
)
}
func QAButtonisOffActive() -> some View {
self
.frame(width: 150, height: 52, alignment: .leading)
.background(
Color("DesignLightYellow")
)
.roundedCorner(12, corners: [.topRight, .topLeft])
.background(
Rectangle()
.roundedCorner(12, corners: [.topRight, .topLeft])
.frame(width: 156, height: 55, alignment: .leading)
.padding(.bottom, 3)
.foregroundColor(Color("DesignLightMutedOrange"))
)
}
}
我建议,显示边框的更好方法是使用
.stroke
在形状周围绘制线条。目前,您的样式在几个不同的地方重复。通过将所有样式放入一个
ButtonStyle
中可以避免这种情况。这可以包括前景文本的样式以及背景和边框的样式。这是一个示例
ButtonStyle
实现,基于您提供的代码:
struct TabButton: ButtonStyle {
let isOn: Bool
let withLeftBorder: Bool
let buttonWidth: CGFloat = 153
let lineWidth: CGFloat = 2
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.title3)
.fontWeight(.bold)
.foregroundStyle(.designMiddleMutedOrange)
.multilineTextAlignment(.center)
.frame(width: buttonWidth, height: 55)
.background {
if isOn {
Rectangle()
.fill(
LinearGradient(
colors: [.FFFAE_6, .designLightYellow],
startPoint: .top,
endPoint: .bottom
)
)
.padding(.bottom, lineWidth)
} else {
Color.designLightYellow
}
}
.clipShape(
UnevenRoundedRectangle(
cornerRadii: .init(
topLeading: withLeftBorder ? 12 : 0,
topTrailing: 12
)
)
)
.overlay(alignment: .trailing) {
UnevenRoundedRectangle(
cornerRadii: .init(
topLeading: withLeftBorder ? 12 : 0,
topTrailing: 12
)
)
.stroke(.designMiddleMutedOrange, lineWidth: lineWidth)
.padding(.bottom, lineWidth / 2)
.frame(width: buttonWidth + (withLeftBorder ? 0 : lineWidth / 2))
}
.overlay(alignment: .bottom) {
if isOn {
Color.designLightYellow
.padding(.leading, withLeftBorder ? lineWidth / 2 : 0)
.padding(.trailing, lineWidth / 2)
.frame(height: lineWidth)
}
}
}
}
它的工作原理如下:
FillStyle
,具体取决于按钮是否打开。
UnevenRoundedRectangle
剪切背景,仅对需要圆化的角进行圆化。
UnevenRoundedRectangle
用于抚摸边框作为覆盖。请注意,以这种方式执行描边时,描边线是半进半出(线的中间位于框架边缘),因此使用填充将其保持在底部边缘内。
alignment: .trailing
来确保右边框不移动。
以下是如何将按钮形状用于两个按钮:
struct ContentView: View {
@State private var selectedTab = 0
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 0) {
Button("行程簡介") { selectedTab = 0 }
.buttonStyle(
TabButton(
isOn: selectedTab == 0,
withLeftBorder: false
)
)
Button("Q&A") { selectedTab = 1 }
.buttonStyle(
TabButton(
isOn: selectedTab == 1,
withLeftBorder: true
)
)
Spacer()
}
.background(alignment: .bottom) {
Color.designMiddleMutedOrange
.frame(height: 2) // = lineWidth of button border
}
Color.designLightYellow
}
.frame(height: 300)
}
}