在swiftui中使用实现类似微信右上角的功能菜单

发布于:2025-04-13 ⋅ 阅读:(16) ⋅ 点赞:(0)

因为要开发iOS版本的pakeplus安装包,所以现在需要添加一些实用的功能,比如加载网页可以复制当前网址,可以使用外部safari打开,或者重新加载这样的功能,所以需要模仿实现右上角的功能菜单。实现逻辑就是使用position来控制菜单位置和三角形位置:

实现的代码如下:

import SwiftUI
import WebKit

struct BottomMenuView: View {
    @State private var selectedTab = 0
    @State private var isShowingDrawer = false
    @State private var isShowingMenu = false
    
    // Define your URLs here
    private let urls = [
        "https://www.baidu.com/",
        "https://juejin.cn/",
        "https://chat.deepseek.com/"
    ]
    
    var body: some View {
        ZStack {
            VStack(spacing: 0) {
                // Top Bar with Menu Button
                HStack {
                    Button(action: {
                        isShowingDrawer = true
                    }) {
                        Image(systemName: "line.3.horizontal")
                            .font(.title2)
                            .foregroundColor(.primary)
                    }
                    
                    Spacer()
                    
                    Text("PakePlus")
                    
                    Spacer()
                    
                    // 自定义菜单按钮
                    Button(action: {
                        isShowingMenu.toggle()
                    }) {
                        Image(systemName: "plus.circle")
                            .font(.title2)
                            .foregroundColor(.primary)
                    }
                }
                .background(Color(.systemBackground))
                .padding(.horizontal)
                .padding(.vertical, 6)
                
                // WebView for the selected URL
                WebView(url: URL(string: urls[selectedTab])!)
                    .edgesIgnoringSafeArea(.top)
                
                // Bottom Tab Bar
                HStack(spacing: 0) {
                    ForEach(0 ..< urls.count, id: \.self) { index in
                        Button(action: {
                            selectedTab = index
                        }) {
                            VStack {
                                Image(systemName: tabIcon(for: index))
                                    .font(.system(size: 20))
                                Text(tabTitle(for: index))
                                    .font(.caption)
                            }
                            .foregroundColor(selectedTab == index ? .blue : .gray)
                            .frame(maxWidth: .infinity)
                            .padding(.vertical, 8)
                        }
                    }
                }
                .background(Color(.systemBackground))
                .overlay(
                    Rectangle()
                        .frame(height: 0.5)
                        .foregroundColor(Color(.systemGray4)),
                    alignment: .top
                )
            }
            
            // 自定义菜单
            if isShowingMenu {
                Image(systemName: "arrowtriangle.up.fill")
                    .resizable()
                    .frame(width: 30, height: 30)
                    .position(x: UIScreen.main.bounds.width - 30, y: 50)
                    .foregroundStyle(Color(.systemGray6))
                VStack(alignment: .trailing, spacing: 0) {
                    Button(action: {
                        // 复制网址动作
                        isShowingMenu = false
                    }) {
                        Text("复制网址")
                            .padding(.horizontal, 16)
                            .padding(.vertical, 8)
                            .frame(width: 100)
                    }
                    .background(Color(.systemGray6))
                    .cornerRadius(8, corners: [.topLeft, .topRight])
                    
                    Button(action: {
                        // 外部打开动作
                        isShowingMenu = false
                    }) {
                        Text("外部打开")
                            .padding(.horizontal, 16)
                            .padding(.vertical, 8)
                            .frame(width: 100)
                    }
                    .background(Color(.systemGray6))
                    
                    Button(action: {
                        // 重新加载动作
                        isShowingMenu = false
                    }) {
                        Text("重新加载")
                            .padding(.horizontal, 16)
                            .padding(.vertical, 8)
                            .frame(width: 100)
                    }
                    .background(Color(.systemGray6))
                    .cornerRadius(8, corners: [.bottomLeft, .bottomRight])
                }
                .position(x: UIScreen.main.bounds.width - 60, y: 100)
                .transition(.opacity)
                .background(Color.white.opacity(0.0001))
            }
            
            // Side Drawer
            SideDrawerView(isShowing: $isShowingDrawer)
        }
        .onTapGesture {
            if isShowingMenu {
                isShowingMenu = false
            }
        }
    }
    
    private func tabIcon(for index: Int) -> String {
        switch index {
        case 0:
            return "house.fill"
        case 1:
            return "star.fill"
        case 2:
            return "play.fill"
        default:
            return "circle.fill"
        }
    }
    
    private func tabTitle(for index: Int) -> String {
        switch index {
        case 0:
            return "Home"
        case 1:
            return "Favorites"
        case 2:
            return "Videos"
        default:
            return "Tab"
        }
    }
}

#Preview {
    BottomMenuView()
}

// Add RoundedCorner extension
struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners,
                                cornerRadii: CGSize(width: radius, height: radius))
        return Path(path.cgPath)
    }
}

extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners))
    }
}