You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中为自定义表情选择器添加肤色选择菜单的实现方案

实现SwiftUI表情选择器的多肤色支持方案

针对你需要的类似iPhone键盘的肤色选择交互,推荐使用**Popover结合自定义菜单视图**的方案,相比Overlay更贴合iOS原生交互逻辑,且无需手动处理布局定位,代码模块化程度更高。以下是具体实现步骤:

1. 扩展表情数据模型

首先需要在你的PSOEmoji模型中添加标记和变体生成逻辑,区分支持肤色修改的表情:

struct PSOEmoji: Identifiable {
    let id = UUID()
    let emoji: String
    let supportsSkinTone: Bool // 标记是否支持肤色调整
    
    // 自动生成所有肤色变体(从浅到深)
    var skinToneVariants: [String] {
        guard supportsSkinTone else { return [emoji] }
        let skinModifiers = ["🏻", "🏼", "🏽", "🏾", "🏿"]
        return skinModifiers.map { emoji + $0 }
    }
}

注意:初始化PSOEmojiCategory时,需要给人物类表情设置supportsSkinTone = true,其他表情设为false。

2. 修改EmojiGridView实现长按弹窗逻辑

在原有的网格代码中,添加状态变量控制弹窗显示,并替换手势逻辑:

private struct EmojiGridView: View {
    var cat: PSOEmojiCategory
    @ScaledMetric(relativeTo: .title2) var minimumSize = 33
    @Binding var selectedEmoji: String
    @Environment(\.dismiss) var dismiss
    
    // 新增状态:控制肤色菜单弹窗
    @State private var currentSkinToneEmoji: PSOEmoji?
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(cat.name)
                .font(.title2)
                .multilineTextAlignment(.leading)
                .padding(.bottom, 4)
                .padding(.top, 20)
            
            LazyVGrid(columns: [GridItem(.adaptive(minimum: minimumSize))], spacing: 12) {
                ForEach(cat.emojis) { emoji in
                    Button(emoji.emoji) {
                        selectedEmoji = emoji.emoji
                        dismiss()
                    }
                    .buttonStyle(PlainButtonStyle())
                    .font(.title)
                    // 长按触发肤色菜单(仅支持肤色的表情生效)
                    .onLongPressGesture {
                        if emoji.supportsSkinTone {
                            currentSkinToneEmoji = emoji
                        } else {
                            selectedEmoji = emoji.emoji
                            dismiss()
                        }
                    }
                    // 绑定弹窗:点击表情变体后关闭并返回选中值
                    .popover(item: $currentSkinToneEmoji, arrowEdge: .bottom) { emoji in
                        SkinToneMenuView(emoji: emoji) { selectedVariant in
                            selectedEmoji = selectedVariant
                            dismiss()
                            currentSkinToneEmoji = nil
                        }
                    }
                }
            }
        }
    }
}

3. 实现自定义肤色选择菜单

单独抽离菜单视图,复用性更强:

private struct SkinToneMenuView: View {
    let emoji: PSOEmoji
    let onSelect: (String) -> Void
    
    var body: some View {
        HStack(spacing: 12) {
            ForEach(emoji.skinToneVariants, id: \.self) { variant in
                Button(variant) {
                    onSelect(variant)
                }
                .font(.title)
                .buttonStyle(PlainButtonStyle())
            }
        }
        .padding(8)
        .background(Color(UIColor.systemBackground))
        .cornerRadius(12)
        .shadow(radius: 4)
    }
}

方案优势

  • 原生交互体验:Popover由系统自动定位到触发按钮下方,带箭头指示,符合用户对iOS键盘表情选择的交互预期;
  • 代码模块化:肤色菜单独立成组件,便于后续修改样式或扩展功能;
  • 逻辑清晰:通过状态变量绑定弹窗显示/隐藏,无需手动处理Overlay的布局和动画逻辑。

内容的提问来源于stack exchange,提问作者the Reverend

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 14:15:29