Last active
August 1, 2020 10:56
-
-
Save rr-codes/906ea63b7fafa6603695e689cf555e49 to your computer and use it in GitHub Desktop.
A SwiftUI emoji picker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // EmojiModel.swift | |
| // Countdown | |
| // | |
| // Created by Richard Robinson on 2020-07-31. | |
| // | |
| import Foundation | |
| fileprivate let emojiCategories = [ | |
| 29 : "Smileys & People", | |
| 30 : "Animals & Nature", | |
| 32 : "Travel & Places", | |
| 33 : "Activity", | |
| 34 : "Objects", | |
| 36 : "Flags" | |
| ] | |
| struct EmojiDatabase: Decodable { | |
| let groups: [EmojiGroup] | |
| let dictionary: [Int: EmojiGroup] | |
| enum CodingKeys: String, CodingKey { | |
| case groups | |
| } | |
| struct EmojiGroup: Decodable, Identifiable { | |
| var id: Int { categoryID } | |
| let emoji: [Emoji] | |
| let categoryID: Int | |
| var name: String { emojiCategories[categoryID]! } | |
| struct Emoji: Decodable, Identifiable { | |
| var id: String { unicode } | |
| let name: String | |
| let unicode: String | |
| } | |
| } | |
| init(from decoder: Decoder) throws { | |
| let values = try decoder.container(keyedBy: CodingKeys.self) | |
| self.groups = try values.decode([EmojiGroup].self, forKey: .groups) | |
| self.dictionary = groups.reduce(into: [:]) { $0[$1.id] = $1 } | |
| } | |
| } | |
| class EmojiDBProvider { | |
| static let shared = EmojiDBProvider( | |
| from: try! String(contentsOfFile: Bundle.main.path(forResource: "emoji", ofType: "json")!) | |
| ) | |
| private let contents: Data | |
| lazy var database: EmojiDatabase = { | |
| try! JSONDecoder().decode(EmojiDatabase.self, from: contents) | |
| }() | |
| init(from contents: String) { | |
| self.contents = contents.data(using: .utf8)! | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // EmojiView.swift | |
| // Countdown | |
| // | |
| // Created by Richard Robinson on 2020-07-31. | |
| // | |
| import SwiftUI | |
| fileprivate struct EmojiGroupView: View { | |
| let gridItems = [GridItem](repeating: .init(.fixed(44)), count: 5) | |
| let group: EmojiDatabase.EmojiGroup | |
| @Binding var selectedEmoji: String | |
| var body: some View { | |
| ScrollView(.horizontal, showsIndicators: false) { | |
| LazyHGrid(rows: gridItems, spacing: 16) { | |
| ForEach(group.emoji) { emoji in | |
| Text(emoji.unicode) | |
| .font(.system(size: 30)) | |
| .onTapGesture { | |
| self.selectedEmoji = emoji.unicode | |
| } | |
| .background( | |
| Circle() | |
| .fill(Color.blue) | |
| .opacity(self.selectedEmoji == emoji.unicode ? 0.2 : 0) | |
| .scaleEffect(1.3) | |
| ) | |
| .accessibility(label: Text(emoji.name)) | |
| } | |
| } | |
| .padding(.horizontal, 20) | |
| } | |
| .padding(.bottom, 13) | |
| .padding(.top, 5) | |
| } | |
| } | |
| fileprivate struct EmojiPickerView: View { | |
| let database: EmojiDatabase | |
| @State private var selectedCategory: Int = 29 | |
| @Binding var selectedEmoji: String | |
| private var selectedGroup: EmojiDatabase.EmojiGroup { | |
| database.dictionary[selectedCategory]! | |
| } | |
| var body: some View { | |
| VStack(alignment: .leading) { | |
| Text(selectedGroup.name) | |
| .font(.caption) | |
| .bold() | |
| .textCase(.uppercase) | |
| .opacity(0.5) | |
| .padding(.leading, 20) | |
| .padding(.top, 25) | |
| EmojiGroupView(group: selectedGroup, selectedEmoji: $selectedEmoji) | |
| Picker("Category", selection: $selectedCategory) { | |
| Text("π").tag(29) | |
| Text("πΆ").tag(30) | |
| Text("π").tag(32) | |
| Text("π").tag(33) | |
| Text("π").tag(34) | |
| Text("π").tag(36) | |
| } | |
| .pickerStyle(SegmentedPickerStyle()) | |
| .padding(.horizontal, 20) | |
| .labelsHidden() | |
| .padding(.bottom, 20) | |
| } | |
| .background( | |
| RoundedRectangle(cornerRadius: 25.0) | |
| .fill(Color.white) | |
| .shadow(color: Color.black.opacity(0.1), radius: 5) | |
| ) | |
| } | |
| } | |
| struct EmojiOverlay: View { | |
| let database: EmojiDatabase | |
| @Binding var isPresented: Bool | |
| @Binding var emoji: String | |
| var body: some View { | |
| GeometryReader { geometry in | |
| ZStack(alignment: .bottom) { | |
| Rectangle() | |
| .fill(Color.black) | |
| .opacity(isPresented ? 0.1 : 0.0) | |
| .transition(.opacity) | |
| .zIndex(1) | |
| if isPresented { | |
| EmojiPickerView(database: database, selectedEmoji: $emoji) | |
| .animation(Animation.spring().speed(2)) | |
| .transition(.move(edge: .bottom)) | |
| .padding(.horizontal, 14) | |
| .zIndex(2) | |
| .padding(.bottom, geometry.safeAreaInsets.bottom) | |
| } | |
| } | |
| .edgesIgnoringSafeArea(.all) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment