[转] SwiftUI Custom Styling
参考链接: https://swiftui-lab.com/custom-styling/6个月前 • 152次点击 • 来自 移动端
标签: SwiftUI
样式对于SwiftUI就好比CSS相对于Web开发,原作者也详细阐述了相关概念
Button Custom Styles
Button一共有两个style协议:ButtonStyle和PrimitiveButtonStyle
struct MyButtonStyleExample: View {
var body: some View {
VStack {
Button("Tap Me!") {
print("button pressed!")
}.buttonStyle(MyButtonStyle(color: .blue))
}
}
}
struct MyButtonStyle: ButtonStyle {
var color: Color = .green
public func makeBody(configuration: MyButtonStyle.Configuration) -> some View {
configuration.label
.foregroundColor(.white)
.padding(15)
.background(RoundedRectangle(cornerRadius: 5).fill(color))
.compositingGroup()
.shadow(color: .black, radius: 3)
.opacity(configuration.isPressed ? 0.5 : 1.0)
.scaleEffect(configuration.isPressed ? 0.8 : 1.0)
}
}
通过实现PrimitiveButtonStyle协议,可以让我们控制按钮事件触发的时机,如下例演示的,当我们长按按钮超过1秒后,才会触发按钮的点击事件,触发后,会显示上方的文字:
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack(spacing: 20) {
Text(text)
Button("Tap Me!") {
self.text = "Action Executed!"
}.buttonStyle(MyPrimitiveButtonStyle(color: .red))
}
}
}
struct MyPrimitiveButtonStyle: PrimitiveButtonStyle {
var color: Color
func makeBody(configuration: PrimitiveButtonStyle.Configuration) -> some View {
MyButton(configuration: configuration, color: color)
}
struct MyButton: View {
@GestureState private var pressed = false
let configuration: PrimitiveButtonStyle.Configuration
let color: Color
var body: some View {
let longPress = LongPressGesture(minimumDuration: 1.0, maximumDistance: 0.0)
.updating($pressed) { value, state, _ in state = value }
.onEnded { _ in
self.configuration.trigger()
}
return configuration.label
.foregroundColor(.white)
.padding(15)
.background(RoundedRectangle(cornerRadius: 5).fill(color))
.compositingGroup()
.shadow(color: .black, radius: 3)
.opacity(pressed ? 0.5 : 1.0)
.scaleEffect(pressed ? 0.8 : 1.0)
.gesture(longPress)
}
}
}
Custom Toggle Style
struct ContentView: View {
@State private var flag = true
var body: some View {
VStack {
Toggle(isOn: $flag) {
Text("Custom Toggle")
}
}
.toggleStyle(MyToggleStyle())
}
}
struct MyToggleStyle: ToggleStyle {
let width: CGFloat = 50
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
configuration.label
ZStack(alignment: configuration.isOn ? .trailing : .leading) {
RoundedRectangle(cornerRadius: 4)
.frame(width: width, height: width / 2)
.foregroundColor(configuration.isOn ? .green : .red)
RoundedRectangle(cornerRadius: 4)
.frame(width: (width / 2) - 4, height: width / 2 - 6)
.padding(4)
.foregroundColor(.white)
.onTapGesture {
withAnimation {
configuration.$isOn.wrappedValue.toggle()
}
}
}
}
}
}
Special Consideration #1: Accessibility Activation Point
略
Special Consideration #2: Form (iOS)
当在Form控件中使用自定义的Toggle,显示与原生Toggle会有差别,如果此部分你认为需要与原生一致,可使用Spacer()填充到label与switch之间
Special Consideration #3: Form (macOS)
macOS存在同样的问题,修复的方式不同,请跳转原文查看
Putting It All Together
原作者贴心的一次性解决以上问题
struct MyToggleStyle2: ToggleStyle {
#if os(iOS)
let width: CGFloat = 50
#else
let width: CGFloat = 38
#endif
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
configuration.label
#if os(iOS)
Spacer()
#endif
ZStack(alignment: configuration.isOn ? .trailing : .leading) {
RoundedRectangle(cornerRadius: 4)
.frame(width: width, height: width / 2)
.foregroundColor(configuration.isOn ? .green : .red)
RoundedRectangle(cornerRadius: 4)
.frame(width: (width / 2) - 4, height: width / 2 - 6)
.padding(4)
.foregroundColor(.white)
.onTapGesture {
withAnimation {
configuration.$isOn.wrappedValue.toggle()
}
}
}
}
.accessibility(activationPoint: configuration.isOn ? UnitPoint(x: 0.25, y: 0.5) : UnitPoint(x: 0.75, y: 0.5))
.alignmentGuide(.leading, computeValue: { d in (d.width - self.width) })
}
}
Styled Custom Views
原作者最后给出了一个自定义View的样式的案例:TripleToggle,其可以切换三种状态
详细代码请查阅:https://gist.github.com/swiftui-lab/4469338fd099285aed2d1fd00f5da745