ButtonUp, ButtonDown 📦
用來當作 Tab Bar Button 彈起與按下的兩個狀態,這兩個只是單純的 View 而已,還不能真的按。

import SwiftUI
import ViewModifiers
public struct ButtonUp: View {
    
    // button properties
    let size: CGFloat      // icon size
    let icon: String       // icon name (SF Symbol)
    
    // public init
    public init(icon: String, size: CGFloat = 32){
        self.icon = icon
        self.size = size
    }
    
    // button body
    public var body: some View {
        ZStack {
            
            // 1. 用漸層為底紙,遮在圖示下面
            Neu.buttonUpIconBackground
                .frame(width: size, height: size)  // 這個大小只夠遮住 icon
            
            // 2. 再鋪上挖洞的方紙
            Rectangle()
                .foregroundColor(Neu.color.cardBackground)         // 上色
                .frame(width: size * 2, height: size * 2)          // 方紙為圖示的兩倍大
                .inverseMask(IconCard(icon, size: size))           // 以圖示挖洞
                // shadow & highlight
                .shadow(color: Neu.color.iconShadow, radius: 6, x: 6, y: 6)
                .shadow(color: .white, radius: 3, x: -3, y: -3)
                .cornerRadius(size * 0.5)                          // 剪圓角
            
        } // container (ZStack)
            // ⭐️ 合併所有圖層(為了幫整個 ZStack 製作陰影)
            .compositingGroup()
            // ⭐️ 讓打光與陰影都「收斂」一點(radius 大,x y 小)
            .shadow(color: Color.white.opacity(0.9), radius: 4, x: -2, y: -2)
            .shadow(color: Neu.color.iconShadow.opacity(0.9), radius: 4, x: 2, y: 2)
    }
}import SwiftUI
import ViewModifiers   // for `.inverseMask`
import Workaround      // for `SystemImage`
public struct ButtonDown: View {
    
    // button properties
    let size: CGFloat      // icon size
    let icon: String       // icon name (SF Symbol)
    
    // public init
    public init(icon: String, size: CGFloat = 32){
        self.icon = icon
        self.size = size
    }
    
    // button body
    public var body: some View {
        
        // 0.5 - 0.125 = 0.375
        let rimMask  = Rectangle().cornerRadius(size * 0.375).padding(size * 0.125)
        
        // ⭐️ container
        return ZStack {
            
            // 1. 鋪上放大一點的圓角方紙
            Rectangle()
                .foregroundColor(Neu.color.cardBackground)         // 上色
                .frame(width: size * 2.25, height: size * 2.25)    // 方紙為圖示的 2.25 倍
                .cornerRadius(size * 0.5)                          // 剪圓角
            
            // 2. 設計「按下按鈕」的陰影效果
            Rectangle()
                
                // 2-1. 紙張上色、放大一點、剪圓角
                .fill(Neu.buttonDownShadow)         // 上色
                .frame(width: size * 2.25, height: size * 2.25)    // 方紙為圖示的 2.25 倍
                .cornerRadius(size * 0.5)                          // 剪圓角
                
                // 2-2. 剪下圓角邊緣 (padding = 0.125 * size)
                .inverseMask(rimMask)
                
                // 2-3. 打光、下陰影
                .shadow(color: Neu.color.iconShadow, radius: 6, x: 6, y: 6)
                .shadow(color: .white, radius: 6, x: -4, y: -4)
                
                // 2-4. 剪掉外圍陰影
                .cornerRadius(size * 0.5)
            
            // 3. 圖示
            Gradient.bottomRight(.black, .purple, .pink, .yellow) // 準備圖示顏色
                .frame(width: size, height: size)                 // 限制圖示大小
                .mask(SystemImage(icon))                          // 剪下圖示的部分
                .font(.system(size: 24, weight: .bold))           // 字體加粗
                .offset(x: 2, y: 2)         // ⭐️ 稍微往右下角移動,「按下的效果」更好
            
        } // container (ZStack)
    }
}Last updated
Was this helpful?