SwiftUI 在Menu中使用客置化MenuStyle
xcode 13.4.1, swift 5.5, iOS 15.4
2022-08-29
在
SwiftUI使用Menu基本使用 知道Menu的基本用法,現在來看看如何使用MenuStyle來改變Menu的外觀。
1. 先創建一個基本的Menu。
import SwiftUI
struct MenuExample : View {
var body: some View {
Menu {
Button {
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
Button {
} label: {
Label("Print", systemImage: "printer")
}
} label: {
Text("Menu Exammple")
}
}
}
2. 以上的Menu只用了一個Text,有時要配合App的設計,需要改變Menu的外觀,比如藍色圓角長方形,實現如下:
import SwiftUI
struct MenuExample : View {
var body: some View {
Menu {
Button {
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
Button {
} label: {
Label("Print", systemImage: "printer")
}
} label: {
Text("Menu Exammple")
.padding()
.foregroundColor(.white)
.background(.blue, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
}
3. 設置Menu的外觀,除了可以像以上直接改變Menu的Label View,另一種方法是創建MenuStyle的子類,下面就是用這種方法實現了藍色圓角長方形,並使用menuStyle指定該子類。
import SwiftUI
struct MenuExample : View {
var body: some View {
Menu {
Button {
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
Button {
} label: {
Label("Print", systemImage: "printer")
}
} label: {
Text("Menu Exammple")
}
.menuStyle(ColorMenuButtonStyle())
}
}
struct ColorMenuButtonStyle : MenuStyle {
func makeBody(configuration: Configuration) -> some View {
Menu(configuration)
.padding()
.foregroundColor(.white)
.background(.blue, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
4. 進一步,我們在ColorMenuButtonStyle增加一個顏色的設置,使得有更多的變化及靈活性。
import SwiftUI
struct MenuExample : View {
var body: some View {
Menu {
Button {
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
Button {
} label: {
Label("Print", systemImage: "printer")
}
} label: {
Text("Menu Exammple")
}
.menuStyle(ColorMenuButtonStyle(color: .mint))
}
}
struct ColorMenuButtonStyle : MenuStyle {
let color : Color
func makeBody(configuration: Configuration) -> some View {
Menu(configuration)
.padding()
.foregroundColor(.white)
.background(color, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
5. 以下使用一個例子來演示可以如何重用ColorMenuButtonStyle,這個例子是選擇Menu的item來決定Menu使用哪個ColorMenuButtonStyle來套用到其menuStyle中,為了不要搞得太複雜,也為了演示如何擴展MenuStyle,我們以下只寫了4種顏色的ColorMenuButtonStyle並將其放到MenuStyle,下面程式用colorMenuButtonStyle變量來記住選擇了那一個ColorMenuButtonStyle,再用Menu的menuStyle方法來套用到Menu中,實作如下:
import SwiftUI
struct MenuExample : View {
@State var colorMenuButtonStyle : ColorMenuButtonStyle = .redColorMenuButtonStyle
let colorMenuButtonStyles : [ ColorMenuButtonStyle ] = [
.redColorMenuButtonStyle, .greenColorMenuButtonStyle,
.blueColorMenuButtonStyle, .mintColorMenuButtonStyle
]
var body: some View {
Menu {
ForEach(colorMenuButtonStyles, id : \.self) { menuButtonStyle in
Button {
colorMenuButtonStyle = menuButtonStyle
} label: {
if menuButtonStyle == colorMenuButtonStyle {
Label(menuButtonStyle.color.description, systemImage: "checkmark")
} else {
Text(menuButtonStyle.color.description)
}
}
}
} label: {
Text("Menu Exammple")
}
.menuStyle(colorMenuButtonStyle)
}
}
struct ColorMenuButtonStyle : MenuStyle, Hashable {
let color : Color
func makeBody(configuration: Configuration) -> some View {
Menu(configuration)
.padding()
.foregroundColor(.white)
.font(.system(size: 28, weight: .bold, design: .rounded))
.background(color, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
extension MenuStyle where Self == ColorMenuButtonStyle {
static var redColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .red) }
static var greenColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .green) }
static var blueColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .blue) }
static var mintColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .mint) }
}
6. 我們還可以用SwiftUI提供的animation來控制切換MenuStyle的速度,實現如下:
import SwiftUI
struct MenuExample : View {
@State var colorMenuButtonStyle : ColorMenuButtonStyle = .redColorMenuButtonStyle
let colorMenuButtonStyles : [ ColorMenuButtonStyle ] = [
.redColorMenuButtonStyle, .greenColorMenuButtonStyle,
.blueColorMenuButtonStyle, .mintColorMenuButtonStyle
]
var body: some View {
Menu {
ForEach(colorMenuButtonStyles, id : \.self) { menuButtonStyle in
Button {
withAnimation(.easeIn(duration: 1)) {
colorMenuButtonStyle = menuButtonStyle
}
} label: {
if menuButtonStyle == colorMenuButtonStyle {
Label(menuButtonStyle.color.description, systemImage: "checkmark")
} else {
Text(menuButtonStyle.color.description)
}
}
}
} label: {
Text("Menu Exammple")
}
.menuStyle(colorMenuButtonStyle)
}
}
struct ColorMenuButtonStyle : MenuStyle, Hashable {
let color : Color
func makeBody(configuration: Configuration) -> some View {
Menu(configuration)
.padding()
.foregroundColor(.white)
.font(.system(size: 28, weight: .bold, design: .rounded))
.background(color, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
extension MenuStyle where Self == ColorMenuButtonStyle {
static var redColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .red) }
static var greenColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .green) }
static var blueColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .blue) }
static var mintColorMenuButtonStyle : ColorMenuButtonStyle { .init(color: .mint) }
}