关于SwiftUI中自定义Button的问题

问题描述 投票:0回答:1

我正在尝试让按钮看起来像这样。

也就是说,按钮会随着点击而改变其显示状态,有点类似于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")) ) } }
    
swift swiftui
1个回答
0
投票
您通过在背景中显示一个比前景中的形状稍大的形状来创建边界线。这就是为什么拐角不完全遵循相同轮廓的原因。

我建议,显示边框的更好方法是使用

.stroke

 在形状周围绘制线条。

目前,您的样式在几个不同的地方重复。通过将所有样式放入一个

ButtonStyle

 中可以避免这种情况。这可以包括前景文本的样式以及背景和边框的样式。

这是一个示例

ButtonStyle

 实现,基于您提供的代码:

struct TabButton: ButtonStyle { let isOn: Bool let isTopLeftCornerRounded: Bool let lineWidth: CGFloat = 2 func makeBody(configuration: Configuration) -> some View { configuration.label .font(.title3) .fontWeight(.bold) .foregroundStyle(.designMiddleMutedOrange) .multilineTextAlignment(.center) .frame(width: 153, height: 55) .background { if isOn { Rectangle() .fill( LinearGradient( colors: [.FFFAE_6, .designLightYellow], startPoint: .top, endPoint: .bottom ) ) } else { Color.designLightYellow } } .clipShape( UnevenRoundedRectangle( cornerRadii: .init( topLeading: isTopLeftCornerRounded ? 12 : 0, topTrailing: 12 ) ) ) .overlay( UnevenRoundedRectangle( cornerRadii: .init( topLeading: isTopLeftCornerRounded ? 12 : 0, topTrailing: 12 ) ) .stroke(.designMiddleMutedOrange, lineWidth: lineWidth) .padding(.bottom, lineWidth / 2) ) .overlay(alignment: .bottom) { if isOn { Color.designLightYellow .frame(height: lineWidth) .padding(.horizontal, lineWidth / 2) } } } }
它的工作原理如下:

    文本标签显示在前景中。
  • 背景会填充相应的
  • FillStyle
    ,具体取决于按钮是否打开。
  • 然后使用
  • UnevenRoundedRectangle
     剪切背景,仅对需要圆化的角进行圆化。
  • 另一个
  • UnevenroundedRectangle
     用于抚摸边框作为覆盖。请注意,以这种方式执行描边时,描边线是半进半出(线的中间位于框架边缘),因此使用填充将其保持在底部边缘内。
  • 如果按钮未打开,边框的下边缘将被背景颜色的覆盖层遮盖。
顺便说一句,我认为你的颜色“#FFFAE6”命名错误,因为我认为它实际上必须更像#fec890。

以下是如何将按钮形状用于两个按钮:

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, isTopLeftCornerRounded: false ) ) Button("Q&A") { selectedTab = 1 } .buttonStyle( TabButton( isOn: selectedTab == 1, isTopLeftCornerRounded: true ) ) Spacer() } .background(alignment: .bottom) { Color.designMiddleMutedOrange .frame(height: 2) // = lineWidth of button border } Color.designLightYellow } .frame(height: 300) } }

Animation

© www.soinside.com 2019 - 2024. All rights reserved.