├── .gitignore ├── .travis.yml ├── Assets ├── EmojiPicker.sketch ├── anchorDarkEmojiTag.png ├── anchorDarkEmojiTag@2x.png ├── anchorDarkEmojiTag@3x.png ├── centerDarkEmojiTag.png ├── centerDarkEmojiTag@2x.png ├── centerDarkEmojiTag@3x.png ├── leftDarkEmojiTag.png ├── leftDarkEmojiTag@2x.png ├── leftDarkEmojiTag@3x.png ├── rightDarkEmojiTag.png ├── rightDarkEmojiTag@2x.png ├── rightDarkEmojiTag@3x.png ├── rightLightEmojiTag.png ├── rightLightEmojiTag@2x.png └── rightLightEmojiTag@3x.png ├── EmojiPicker.podspec ├── EmojiPicker.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── EmojiPicker.xcscheme │ └── EmojiPickerTests.xcscheme ├── EmojiPicker ├── EmojiPicker.h ├── EmojiPicker.swift ├── Engine │ ├── Extension │ │ ├── String.swift │ │ ├── UIButton.swift │ │ └── UIColor.swift │ └── Utility │ │ ├── EmojiPreviewer.swift │ │ ├── EmojiPreviewer.xib │ │ ├── UserDefaultsProtocol.swift │ │ └── Vibrator.swift ├── Info.plist ├── Model │ ├── Category.swift │ └── Emoji.swift ├── Resource │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── activity.imageset │ │ │ ├── Contents.json │ │ │ ├── activity.png │ │ │ ├── activity@2x.png │ │ │ └── activity@3x.png │ │ ├── anchorDarkEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── anchorDarkEmojiTag.png │ │ │ ├── anchorDarkEmojiTag@2x.png │ │ │ └── anchorDarkEmojiTag@3x.png │ │ ├── anchorLightEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── anchorLightEmojiTag.png │ │ │ ├── anchorLightEmojiTag@2x.png │ │ │ └── anchorLightEmojiTag@3x.png │ │ ├── animalsAndNature.imageset │ │ │ ├── Contents.json │ │ │ ├── animalsAndNature.png │ │ │ ├── animalsAndNature@2x.png │ │ │ └── animalsAndNature@3x.png │ │ ├── centerDarkEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── centerDarkEmojiTag.png │ │ │ ├── centerDarkEmojiTag@2x.png │ │ │ └── centerDarkEmojiTag@3x.png │ │ ├── centerLightEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── centerLightEmojiTag.png │ │ │ ├── centerLightEmojiTag@2x.png │ │ │ └── centerLightEmojiTag@3x.png │ │ ├── darkEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── darkEmojiTag.png │ │ │ ├── darkEmojiTag@2x.png │ │ │ └── darkEmojiTag@3x.png │ │ ├── flags.imageset │ │ │ ├── Contents.json │ │ │ ├── flags.png │ │ │ ├── flags@2x.png │ │ │ └── flags@3x.png │ │ ├── foodAndDrink.imageset │ │ │ ├── Contents.json │ │ │ ├── foodAndDrink.png │ │ │ ├── foodAndDrink@2x.png │ │ │ └── foodAndDrink@3x.png │ │ ├── frequentlyUsed.imageset │ │ │ ├── Contents.json │ │ │ ├── frequentlyUsed.png │ │ │ ├── frequentlyUsed@2x.png │ │ │ └── frequentlyUsed@3x.png │ │ ├── leftDarkEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── leftDarkEmojiTag.png │ │ │ ├── leftDarkEmojiTag@2x.png │ │ │ └── leftDarkEmojiTag@3x.png │ │ ├── leftLightEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── leftLightEmojiTag.png │ │ │ ├── leftLightEmojiTag@2x.png │ │ │ └── leftLightEmojiTag@3x.png │ │ ├── lightEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── lightEmojiTag.png │ │ │ ├── lightEmojiTag@2x.png │ │ │ └── lightEmojiTag@3x.png │ │ ├── objects.imageset │ │ │ ├── Contents.json │ │ │ ├── objects.png │ │ │ ├── objects@2x.png │ │ │ └── objects@3x.png │ │ ├── rightDarkEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── rightDarkEmojiTag.png │ │ │ ├── rightDarkEmojiTag@2x.png │ │ │ └── rightDarkEmojiTag@3x.png │ │ ├── rightLightEmojiTag.imageset │ │ │ ├── Contents.json │ │ │ ├── rightLightEmojiTag.png │ │ │ ├── rightLightEmojiTag@2x.png │ │ │ └── rightLightEmojiTag@3x.png │ │ ├── smileysAndPeople.imageset │ │ │ ├── Contents.json │ │ │ ├── smileysAndPeople.png │ │ │ ├── smileysAndPeople@2x.png │ │ │ └── smileysAndPeople@3x.png │ │ ├── symbols.imageset │ │ │ ├── Contents.json │ │ │ ├── symbols.png │ │ │ ├── symbols@2x.png │ │ │ └── symbols@3x.png │ │ └── travelAndPlaces.imageset │ │ │ ├── Contents.json │ │ │ ├── travelAndPlaces.png │ │ │ ├── travelAndPlaces@2x.png │ │ │ └── travelAndPlaces@3x.png │ ├── emojis.json │ ├── emojis11.0.1.json │ ├── emojis9.1.json │ ├── en.lproj │ │ └── Localizable.strings │ └── vi.lproj │ │ └── Localizable.strings ├── Storyboard │ ├── EmojiPicker.storyboard │ └── EmojiPopover.storyboard ├── View │ ├── EmojiPicker │ │ └── EmojiPickerViewController.swift │ └── EmojiPopover │ │ ├── EmojiCollectionViewCell.swift │ │ ├── EmojiCollectionViewCell.xib │ │ ├── EmojiHeaderView.swift │ │ ├── EmojiHeaderView.xib │ │ ├── EmojiPopoverViewController.swift │ │ ├── GroupCollectionViewCell.swift │ │ ├── GroupCollectionViewCell.xib │ │ ├── GroupHeaderView.swift │ │ └── GroupHeaderView.xib └── ViewModel │ └── EmojiPickerViewModel.swift ├── EmojiPickerTests ├── EmojiPickerTests.swift └── Info.plist ├── LICENSE ├── README.md ├── icon.png ├── ipad-example.gif └── iphone-example.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_project: EmojiPicker.xcodeproj # path to your xcodeproj folder 3 | xcode_scheme: EmojiPickerTests 4 | xcode_destination: platform=iOS Simulator,OS=10.1,name=iPad Pro (9.7-inch) -------------------------------------------------------------------------------- /Assets/EmojiPicker.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/EmojiPicker.sketch -------------------------------------------------------------------------------- /Assets/anchorDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/anchorDarkEmojiTag.png -------------------------------------------------------------------------------- /Assets/anchorDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/anchorDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /Assets/anchorDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/anchorDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /Assets/centerDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/centerDarkEmojiTag.png -------------------------------------------------------------------------------- /Assets/centerDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/centerDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /Assets/centerDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/centerDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /Assets/leftDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/leftDarkEmojiTag.png -------------------------------------------------------------------------------- /Assets/leftDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/leftDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /Assets/leftDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/leftDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /Assets/rightDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightDarkEmojiTag.png -------------------------------------------------------------------------------- /Assets/rightDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /Assets/rightDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /Assets/rightLightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightLightEmojiTag.png -------------------------------------------------------------------------------- /Assets/rightLightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightLightEmojiTag@2x.png -------------------------------------------------------------------------------- /Assets/rightLightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/Assets/rightLightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'EmojiPicker' 3 | s.version = '1.1' 4 | s.summary = 'Emoji Picker' 5 | s.description = <<-DESC 6 | Emoji picker written on Swift 4.2 by levantAJ 7 | DESC 8 | s.homepage = 'https://github.com/levantAJ/EmojiPicker' 9 | s.license = { :type => 'MIT', :file => 'LICENSE' } 10 | s.author = { 'Tai Le' => 'sirlevantai@gmail.com' } 11 | s.source = { :git => 'https://github.com/levantAJ/EmojiPicker.git', :tag => s.version.to_s } 12 | s.ios.deployment_target = '9.0' 13 | s.swift_version = '4.2' 14 | s.source_files = 'EmojiPicker/**/*.{swift}' 15 | s.resources = "EmojiPicker/**/*.{storyboard,xib,json,xcassets,lproj}" 16 | 17 | end -------------------------------------------------------------------------------- /EmojiPicker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3303BE90219C417900B38800 /* EmojiPreviewer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3303BE8F219C417900B38800 /* EmojiPreviewer.xib */; }; 11 | 3303BE92219D49E700B38800 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3303BE91219D49E700B38800 /* UIButton.swift */; }; 12 | 3303BE94219D4B7E00B38800 /* EmojiPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3303BE93219D4B7E00B38800 /* EmojiPickerViewController.swift */; }; 13 | 3303BE9D219D5F4300B38800 /* EmojiPopover.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3303BE9C219D5F4300B38800 /* EmojiPopover.storyboard */; }; 14 | 3327619621999B9600433FD4 /* EmojiHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3327619521999B9600433FD4 /* EmojiHeaderView.swift */; }; 15 | 3327619821999BA100433FD4 /* EmojiHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3327619721999BA000433FD4 /* EmojiHeaderView.xib */; }; 16 | 334EE9362199C464000404B8 /* UserDefaultsProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 334EE9352199C464000404B8 /* UserDefaultsProtocol.swift */; }; 17 | 337131AD21991D4900838035 /* EmojiPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 337131AC21991D4900838035 /* EmojiPicker.swift */; }; 18 | 337131B321993C1E00838035 /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 337131B221993C1E00838035 /* EmojiCollectionViewCell.swift */; }; 19 | 337131B621993D3100838035 /* EmojiPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 337131B521993D3100838035 /* EmojiPickerViewModel.swift */; }; 20 | 337131B92199487B00838035 /* EmojiCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 337131B82199487B00838035 /* EmojiCollectionViewCell.xib */; }; 21 | 337131C121995CFC00838035 /* emojis.json in Resources */ = {isa = PBXBuildFile; fileRef = 337131C021995CFC00838035 /* emojis.json */; }; 22 | 3372D2DE219EFB210098C21A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3372D2DD219EFB210098C21A /* Emoji.swift */; }; 23 | 338155FC21995DCB0059EF2F /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338155FB21995DCB0059EF2F /* Category.swift */; }; 24 | 33815600219973DE0059EF2F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 338155FF219973DE0059EF2F /* Assets.xcassets */; }; 25 | 33815603219975C30059EF2F /* GroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33815601219975C30059EF2F /* GroupCollectionViewCell.swift */; }; 26 | 33815604219975C30059EF2F /* GroupCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33815602219975C30059EF2F /* GroupCollectionViewCell.xib */; }; 27 | 33815606219981000059EF2F /* GroupHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33815605219981000059EF2F /* GroupHeaderView.swift */; }; 28 | 33815608219981160059EF2F /* GroupHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33815607219981160059EF2F /* GroupHeaderView.xib */; }; 29 | 3381560B219982E30059EF2F /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3381560A219982E30059EF2F /* String.swift */; }; 30 | 33D2B500219906C300D528EC /* EmojiPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D2B4F6219906C300D528EC /* EmojiPicker.framework */; }; 31 | 33D2B505219906C300D528EC /* EmojiPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D2B504219906C300D528EC /* EmojiPickerTests.swift */; }; 32 | 33D2B507219906C300D528EC /* EmojiPicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 33D2B4F9219906C300D528EC /* EmojiPicker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | 33D2B514219908DC00D528EC /* EmojiPicker.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 33D2B513219908DC00D528EC /* EmojiPicker.storyboard */; }; 34 | 33D2B5172199094800D528EC /* EmojiPopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D2B5162199094800D528EC /* EmojiPopoverViewController.swift */; }; 35 | 33D716A2219A425B00599BDD /* Vibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D716A1219A425B00599BDD /* Vibrator.swift */; }; 36 | 33D716A4219A746C00599BDD /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D716A3219A746C00599BDD /* UIColor.swift */; }; 37 | 33D716A7219A814400599BDD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33D716A9219A814400599BDD /* Localizable.strings */; }; 38 | 33D716B0219ACB6500599BDD /* EmojiPreviewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D716AF219ACB6500599BDD /* EmojiPreviewer.swift */; }; 39 | 33EA6B49219EBB8F00DDB012 /* emojis9.1.json in Resources */ = {isa = PBXBuildFile; fileRef = 33EA6B48219EBB8E00DDB012 /* emojis9.1.json */; }; 40 | 33EA6B4B219EC7A700DDB012 /* emojis11.0.1.json in Resources */ = {isa = PBXBuildFile; fileRef = 33EA6B4A219EC7A700DDB012 /* emojis11.0.1.json */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXContainerItemProxy section */ 44 | 33D2B501219906C300D528EC /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = 33D2B4ED219906C300D528EC /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = 33D2B4F5219906C300D528EC; 49 | remoteInfo = EmojiPicker; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | 3303BE8F219C417900B38800 /* EmojiPreviewer.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiPreviewer.xib; sourceTree = ""; }; 55 | 3303BE91219D49E700B38800 /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; 56 | 3303BE93219D4B7E00B38800 /* EmojiPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewController.swift; sourceTree = ""; }; 57 | 3303BE9C219D5F4300B38800 /* EmojiPopover.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = EmojiPopover.storyboard; sourceTree = ""; }; 58 | 3327619521999B9600433FD4 /* EmojiHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiHeaderView.swift; sourceTree = ""; }; 59 | 3327619721999BA000433FD4 /* EmojiHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiHeaderView.xib; sourceTree = ""; }; 60 | 334EE9352199C464000404B8 /* UserDefaultsProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsProtocol.swift; sourceTree = ""; }; 61 | 337131AC21991D4900838035 /* EmojiPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPicker.swift; sourceTree = ""; }; 62 | 337131B221993C1E00838035 /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = ""; }; 63 | 337131B521993D3100838035 /* EmojiPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewModel.swift; sourceTree = ""; }; 64 | 337131B82199487B00838035 /* EmojiCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiCollectionViewCell.xib; sourceTree = ""; }; 65 | 337131C021995CFC00838035 /* emojis.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = emojis.json; sourceTree = ""; }; 66 | 3372D2DD219EFB210098C21A /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = ""; }; 67 | 338155FB21995DCB0059EF2F /* Category.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = ""; }; 68 | 338155FF219973DE0059EF2F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 69 | 33815601219975C30059EF2F /* GroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupCollectionViewCell.swift; sourceTree = ""; }; 70 | 33815602219975C30059EF2F /* GroupCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GroupCollectionViewCell.xib; sourceTree = ""; }; 71 | 33815605219981000059EF2F /* GroupHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupHeaderView.swift; sourceTree = ""; }; 72 | 33815607219981160059EF2F /* GroupHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GroupHeaderView.xib; sourceTree = ""; }; 73 | 3381560A219982E30059EF2F /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 74 | 33D2B4F6219906C300D528EC /* EmojiPicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EmojiPicker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | 33D2B4F9219906C300D528EC /* EmojiPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmojiPicker.h; sourceTree = ""; }; 76 | 33D2B4FA219906C300D528EC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | 33D2B4FF219906C300D528EC /* EmojiPickerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EmojiPickerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | 33D2B504219906C300D528EC /* EmojiPickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerTests.swift; sourceTree = ""; }; 79 | 33D2B506219906C300D528EC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | 33D2B513219908DC00D528EC /* EmojiPicker.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = EmojiPicker.storyboard; sourceTree = ""; }; 81 | 33D2B5162199094800D528EC /* EmojiPopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPopoverViewController.swift; sourceTree = ""; }; 82 | 33D716A1219A425B00599BDD /* Vibrator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vibrator.swift; sourceTree = ""; }; 83 | 33D716A3219A746C00599BDD /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 84 | 33D716A8219A814400599BDD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 85 | 33D716AA219A815000599BDD /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 86 | 33D716AF219ACB6500599BDD /* EmojiPreviewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPreviewer.swift; sourceTree = ""; }; 87 | 33EA6B48219EBB8E00DDB012 /* emojis9.1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = emojis9.1.json; sourceTree = ""; }; 88 | 33EA6B4A219EC7A700DDB012 /* emojis11.0.1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = emojis11.0.1.json; sourceTree = ""; }; 89 | /* End PBXFileReference section */ 90 | 91 | /* Begin PBXFrameworksBuildPhase section */ 92 | 33D2B4F3219906C300D528EC /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | 33D2B4FC219906C300D528EC /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | 33D2B500219906C300D528EC /* EmojiPicker.framework in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | /* End PBXFrameworksBuildPhase section */ 108 | 109 | /* Begin PBXGroup section */ 110 | 3303BE97219D5E5500B38800 /* EmojiPopover */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 33D2B5162199094800D528EC /* EmojiPopoverViewController.swift */, 114 | 337131B221993C1E00838035 /* EmojiCollectionViewCell.swift */, 115 | 337131B82199487B00838035 /* EmojiCollectionViewCell.xib */, 116 | 33815601219975C30059EF2F /* GroupCollectionViewCell.swift */, 117 | 33815602219975C30059EF2F /* GroupCollectionViewCell.xib */, 118 | 33815605219981000059EF2F /* GroupHeaderView.swift */, 119 | 33815607219981160059EF2F /* GroupHeaderView.xib */, 120 | 3327619521999B9600433FD4 /* EmojiHeaderView.swift */, 121 | 3327619721999BA000433FD4 /* EmojiHeaderView.xib */, 122 | ); 123 | path = EmojiPopover; 124 | sourceTree = ""; 125 | }; 126 | 3303BE98219D5E5E00B38800 /* EmojiPicker */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 3303BE93219D4B7E00B38800 /* EmojiPickerViewController.swift */, 130 | ); 131 | path = EmojiPicker; 132 | sourceTree = ""; 133 | }; 134 | 334EE9332199C446000404B8 /* Engine */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 334EE9342199C453000404B8 /* Utility */, 138 | 33815609219982D70059EF2F /* Extension */, 139 | ); 140 | path = Engine; 141 | sourceTree = ""; 142 | }; 143 | 334EE9342199C453000404B8 /* Utility */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 334EE9352199C464000404B8 /* UserDefaultsProtocol.swift */, 147 | 33D716A1219A425B00599BDD /* Vibrator.swift */, 148 | 33D716AF219ACB6500599BDD /* EmojiPreviewer.swift */, 149 | 3303BE8F219C417900B38800 /* EmojiPreviewer.xib */, 150 | ); 151 | path = Utility; 152 | sourceTree = ""; 153 | }; 154 | 337131B421993D2500838035 /* ViewModel */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 337131B521993D3100838035 /* EmojiPickerViewModel.swift */, 158 | ); 159 | path = ViewModel; 160 | sourceTree = ""; 161 | }; 162 | 337131BD21995AB200838035 /* Resource */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 337131C021995CFC00838035 /* emojis.json */, 166 | 33EA6B48219EBB8E00DDB012 /* emojis9.1.json */, 167 | 33EA6B4A219EC7A700DDB012 /* emojis11.0.1.json */, 168 | 338155FF219973DE0059EF2F /* Assets.xcassets */, 169 | 33D716A9219A814400599BDD /* Localizable.strings */, 170 | ); 171 | path = Resource; 172 | sourceTree = ""; 173 | }; 174 | 337131C221995D8900838035 /* Model */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 338155FB21995DCB0059EF2F /* Category.swift */, 178 | 3372D2DD219EFB210098C21A /* Emoji.swift */, 179 | ); 180 | path = Model; 181 | sourceTree = ""; 182 | }; 183 | 33815609219982D70059EF2F /* Extension */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 3381560A219982E30059EF2F /* String.swift */, 187 | 33D716A3219A746C00599BDD /* UIColor.swift */, 188 | 3303BE91219D49E700B38800 /* UIButton.swift */, 189 | ); 190 | path = Extension; 191 | sourceTree = ""; 192 | }; 193 | 33D2B4EC219906C300D528EC = { 194 | isa = PBXGroup; 195 | children = ( 196 | 33D2B4F8219906C300D528EC /* EmojiPicker */, 197 | 33D2B503219906C300D528EC /* EmojiPickerTests */, 198 | 33D2B4F7219906C300D528EC /* Products */, 199 | ); 200 | sourceTree = ""; 201 | }; 202 | 33D2B4F7219906C300D528EC /* Products */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 33D2B4F6219906C300D528EC /* EmojiPicker.framework */, 206 | 33D2B4FF219906C300D528EC /* EmojiPickerTests.xctest */, 207 | ); 208 | name = Products; 209 | sourceTree = ""; 210 | }; 211 | 33D2B4F8219906C300D528EC /* EmojiPicker */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 334EE9332199C446000404B8 /* Engine */, 215 | 337131C221995D8900838035 /* Model */, 216 | 337131B421993D2500838035 /* ViewModel */, 217 | 33D2B5152199093800D528EC /* View */, 218 | 33D2B512219908C700D528EC /* Storyboard */, 219 | 337131BD21995AB200838035 /* Resource */, 220 | 33D2B4F9219906C300D528EC /* EmojiPicker.h */, 221 | 337131AC21991D4900838035 /* EmojiPicker.swift */, 222 | 33D2B4FA219906C300D528EC /* Info.plist */, 223 | ); 224 | path = EmojiPicker; 225 | sourceTree = ""; 226 | }; 227 | 33D2B503219906C300D528EC /* EmojiPickerTests */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 33D2B504219906C300D528EC /* EmojiPickerTests.swift */, 231 | 33D2B506219906C300D528EC /* Info.plist */, 232 | ); 233 | path = EmojiPickerTests; 234 | sourceTree = ""; 235 | }; 236 | 33D2B512219908C700D528EC /* Storyboard */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 33D2B513219908DC00D528EC /* EmojiPicker.storyboard */, 240 | 3303BE9C219D5F4300B38800 /* EmojiPopover.storyboard */, 241 | ); 242 | path = Storyboard; 243 | sourceTree = ""; 244 | }; 245 | 33D2B5152199093800D528EC /* View */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | 3303BE98219D5E5E00B38800 /* EmojiPicker */, 249 | 3303BE97219D5E5500B38800 /* EmojiPopover */, 250 | ); 251 | path = View; 252 | sourceTree = ""; 253 | }; 254 | /* End PBXGroup section */ 255 | 256 | /* Begin PBXHeadersBuildPhase section */ 257 | 33D2B4F1219906C300D528EC /* Headers */ = { 258 | isa = PBXHeadersBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | 33D2B507219906C300D528EC /* EmojiPicker.h in Headers */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXHeadersBuildPhase section */ 266 | 267 | /* Begin PBXNativeTarget section */ 268 | 33D2B4F5219906C300D528EC /* EmojiPicker */ = { 269 | isa = PBXNativeTarget; 270 | buildConfigurationList = 33D2B50A219906C300D528EC /* Build configuration list for PBXNativeTarget "EmojiPicker" */; 271 | buildPhases = ( 272 | 33D2B4F1219906C300D528EC /* Headers */, 273 | 33D2B4F2219906C300D528EC /* Sources */, 274 | 33D2B4F3219906C300D528EC /* Frameworks */, 275 | 33D2B4F4219906C300D528EC /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | ); 281 | name = EmojiPicker; 282 | productName = EmojiPicker; 283 | productReference = 33D2B4F6219906C300D528EC /* EmojiPicker.framework */; 284 | productType = "com.apple.product-type.framework"; 285 | }; 286 | 33D2B4FE219906C300D528EC /* EmojiPickerTests */ = { 287 | isa = PBXNativeTarget; 288 | buildConfigurationList = 33D2B50D219906C300D528EC /* Build configuration list for PBXNativeTarget "EmojiPickerTests" */; 289 | buildPhases = ( 290 | 33D2B4FB219906C300D528EC /* Sources */, 291 | 33D2B4FC219906C300D528EC /* Frameworks */, 292 | 33D2B4FD219906C300D528EC /* Resources */, 293 | ); 294 | buildRules = ( 295 | ); 296 | dependencies = ( 297 | 33D2B502219906C300D528EC /* PBXTargetDependency */, 298 | ); 299 | name = EmojiPickerTests; 300 | productName = EmojiPickerTests; 301 | productReference = 33D2B4FF219906C300D528EC /* EmojiPickerTests.xctest */; 302 | productType = "com.apple.product-type.bundle.unit-test"; 303 | }; 304 | /* End PBXNativeTarget section */ 305 | 306 | /* Begin PBXProject section */ 307 | 33D2B4ED219906C300D528EC /* Project object */ = { 308 | isa = PBXProject; 309 | attributes = { 310 | LastSwiftUpdateCheck = 1000; 311 | LastUpgradeCheck = 1000; 312 | ORGANIZATIONNAME = levantAJ; 313 | TargetAttributes = { 314 | 33D2B4F5219906C300D528EC = { 315 | CreatedOnToolsVersion = 10.0; 316 | LastSwiftMigration = 1000; 317 | }; 318 | 33D2B4FE219906C300D528EC = { 319 | CreatedOnToolsVersion = 10.0; 320 | }; 321 | }; 322 | }; 323 | buildConfigurationList = 33D2B4F0219906C300D528EC /* Build configuration list for PBXProject "EmojiPicker" */; 324 | compatibilityVersion = "Xcode 9.3"; 325 | developmentRegion = en; 326 | hasScannedForEncodings = 0; 327 | knownRegions = ( 328 | en, 329 | vi, 330 | ); 331 | mainGroup = 33D2B4EC219906C300D528EC; 332 | productRefGroup = 33D2B4F7219906C300D528EC /* Products */; 333 | projectDirPath = ""; 334 | projectRoot = ""; 335 | targets = ( 336 | 33D2B4F5219906C300D528EC /* EmojiPicker */, 337 | 33D2B4FE219906C300D528EC /* EmojiPickerTests */, 338 | ); 339 | }; 340 | /* End PBXProject section */ 341 | 342 | /* Begin PBXResourcesBuildPhase section */ 343 | 33D2B4F4219906C300D528EC /* Resources */ = { 344 | isa = PBXResourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | 33D2B514219908DC00D528EC /* EmojiPicker.storyboard in Resources */, 348 | 33815604219975C30059EF2F /* GroupCollectionViewCell.xib in Resources */, 349 | 3303BE9D219D5F4300B38800 /* EmojiPopover.storyboard in Resources */, 350 | 33EA6B4B219EC7A700DDB012 /* emojis11.0.1.json in Resources */, 351 | 33EA6B49219EBB8F00DDB012 /* emojis9.1.json in Resources */, 352 | 337131C121995CFC00838035 /* emojis.json in Resources */, 353 | 33815600219973DE0059EF2F /* Assets.xcassets in Resources */, 354 | 3303BE90219C417900B38800 /* EmojiPreviewer.xib in Resources */, 355 | 3327619821999BA100433FD4 /* EmojiHeaderView.xib in Resources */, 356 | 33D716A7219A814400599BDD /* Localizable.strings in Resources */, 357 | 337131B92199487B00838035 /* EmojiCollectionViewCell.xib in Resources */, 358 | 33815608219981160059EF2F /* GroupHeaderView.xib in Resources */, 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | 33D2B4FD219906C300D528EC /* Resources */ = { 363 | isa = PBXResourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | /* End PBXResourcesBuildPhase section */ 370 | 371 | /* Begin PBXSourcesBuildPhase section */ 372 | 33D2B4F2219906C300D528EC /* Sources */ = { 373 | isa = PBXSourcesBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | 33815603219975C30059EF2F /* GroupCollectionViewCell.swift in Sources */, 377 | 33D716A4219A746C00599BDD /* UIColor.swift in Sources */, 378 | 338155FC21995DCB0059EF2F /* Category.swift in Sources */, 379 | 3303BE94219D4B7E00B38800 /* EmojiPickerViewController.swift in Sources */, 380 | 3327619621999B9600433FD4 /* EmojiHeaderView.swift in Sources */, 381 | 33815606219981000059EF2F /* GroupHeaderView.swift in Sources */, 382 | 334EE9362199C464000404B8 /* UserDefaultsProtocol.swift in Sources */, 383 | 3303BE92219D49E700B38800 /* UIButton.swift in Sources */, 384 | 33D716B0219ACB6500599BDD /* EmojiPreviewer.swift in Sources */, 385 | 33D2B5172199094800D528EC /* EmojiPopoverViewController.swift in Sources */, 386 | 3372D2DE219EFB210098C21A /* Emoji.swift in Sources */, 387 | 337131B321993C1E00838035 /* EmojiCollectionViewCell.swift in Sources */, 388 | 337131AD21991D4900838035 /* EmojiPicker.swift in Sources */, 389 | 3381560B219982E30059EF2F /* String.swift in Sources */, 390 | 337131B621993D3100838035 /* EmojiPickerViewModel.swift in Sources */, 391 | 33D716A2219A425B00599BDD /* Vibrator.swift in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | 33D2B4FB219906C300D528EC /* Sources */ = { 396 | isa = PBXSourcesBuildPhase; 397 | buildActionMask = 2147483647; 398 | files = ( 399 | 33D2B505219906C300D528EC /* EmojiPickerTests.swift in Sources */, 400 | ); 401 | runOnlyForDeploymentPostprocessing = 0; 402 | }; 403 | /* End PBXSourcesBuildPhase section */ 404 | 405 | /* Begin PBXTargetDependency section */ 406 | 33D2B502219906C300D528EC /* PBXTargetDependency */ = { 407 | isa = PBXTargetDependency; 408 | target = 33D2B4F5219906C300D528EC /* EmojiPicker */; 409 | targetProxy = 33D2B501219906C300D528EC /* PBXContainerItemProxy */; 410 | }; 411 | /* End PBXTargetDependency section */ 412 | 413 | /* Begin PBXVariantGroup section */ 414 | 33D716A9219A814400599BDD /* Localizable.strings */ = { 415 | isa = PBXVariantGroup; 416 | children = ( 417 | 33D716A8219A814400599BDD /* en */, 418 | 33D716AA219A815000599BDD /* vi */, 419 | ); 420 | name = Localizable.strings; 421 | sourceTree = ""; 422 | }; 423 | /* End PBXVariantGroup section */ 424 | 425 | /* Begin XCBuildConfiguration section */ 426 | 33D2B508219906C300D528EC /* Debug */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | ALWAYS_SEARCH_USER_PATHS = NO; 430 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 431 | CLANG_ANALYZER_NONNULL = YES; 432 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 434 | CLANG_CXX_LIBRARY = "libc++"; 435 | CLANG_ENABLE_MODULES = YES; 436 | CLANG_ENABLE_OBJC_ARC = YES; 437 | CLANG_ENABLE_OBJC_WEAK = YES; 438 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 439 | CLANG_WARN_BOOL_CONVERSION = YES; 440 | CLANG_WARN_COMMA = YES; 441 | CLANG_WARN_CONSTANT_CONVERSION = YES; 442 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 443 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 444 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 445 | CLANG_WARN_EMPTY_BODY = YES; 446 | CLANG_WARN_ENUM_CONVERSION = YES; 447 | CLANG_WARN_INFINITE_RECURSION = YES; 448 | CLANG_WARN_INT_CONVERSION = YES; 449 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 451 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 453 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 454 | CLANG_WARN_STRICT_PROTOTYPES = YES; 455 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 456 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 457 | CLANG_WARN_UNREACHABLE_CODE = YES; 458 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 459 | CODE_SIGN_IDENTITY = "iPhone Developer"; 460 | COPY_PHASE_STRIP = NO; 461 | CURRENT_PROJECT_VERSION = 1; 462 | DEBUG_INFORMATION_FORMAT = dwarf; 463 | ENABLE_STRICT_OBJC_MSGSEND = YES; 464 | ENABLE_TESTABILITY = YES; 465 | GCC_C_LANGUAGE_STANDARD = gnu11; 466 | GCC_DYNAMIC_NO_PIC = NO; 467 | GCC_NO_COMMON_BLOCKS = YES; 468 | GCC_OPTIMIZATION_LEVEL = 0; 469 | GCC_PREPROCESSOR_DEFINITIONS = ( 470 | "DEBUG=1", 471 | "$(inherited)", 472 | ); 473 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 474 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 475 | GCC_WARN_UNDECLARED_SELECTOR = YES; 476 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 477 | GCC_WARN_UNUSED_FUNCTION = YES; 478 | GCC_WARN_UNUSED_VARIABLE = YES; 479 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 480 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 481 | MTL_FAST_MATH = YES; 482 | ONLY_ACTIVE_ARCH = YES; 483 | SDKROOT = iphoneos; 484 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 485 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 486 | VERSIONING_SYSTEM = "apple-generic"; 487 | VERSION_INFO_PREFIX = ""; 488 | }; 489 | name = Debug; 490 | }; 491 | 33D2B509219906C300D528EC /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ALWAYS_SEARCH_USER_PATHS = NO; 495 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 496 | CLANG_ANALYZER_NONNULL = YES; 497 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 498 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 499 | CLANG_CXX_LIBRARY = "libc++"; 500 | CLANG_ENABLE_MODULES = YES; 501 | CLANG_ENABLE_OBJC_ARC = YES; 502 | CLANG_ENABLE_OBJC_WEAK = YES; 503 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 504 | CLANG_WARN_BOOL_CONVERSION = YES; 505 | CLANG_WARN_COMMA = YES; 506 | CLANG_WARN_CONSTANT_CONVERSION = YES; 507 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 508 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 509 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 510 | CLANG_WARN_EMPTY_BODY = YES; 511 | CLANG_WARN_ENUM_CONVERSION = YES; 512 | CLANG_WARN_INFINITE_RECURSION = YES; 513 | CLANG_WARN_INT_CONVERSION = YES; 514 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 515 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 516 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 517 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 518 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 519 | CLANG_WARN_STRICT_PROTOTYPES = YES; 520 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 521 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | CODE_SIGN_IDENTITY = "iPhone Developer"; 525 | COPY_PHASE_STRIP = NO; 526 | CURRENT_PROJECT_VERSION = 1; 527 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 528 | ENABLE_NS_ASSERTIONS = NO; 529 | ENABLE_STRICT_OBJC_MSGSEND = YES; 530 | GCC_C_LANGUAGE_STANDARD = gnu11; 531 | GCC_NO_COMMON_BLOCKS = YES; 532 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 533 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 534 | GCC_WARN_UNDECLARED_SELECTOR = YES; 535 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 536 | GCC_WARN_UNUSED_FUNCTION = YES; 537 | GCC_WARN_UNUSED_VARIABLE = YES; 538 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 539 | MTL_ENABLE_DEBUG_INFO = NO; 540 | MTL_FAST_MATH = YES; 541 | SDKROOT = iphoneos; 542 | SWIFT_COMPILATION_MODE = wholemodule; 543 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 544 | VALIDATE_PRODUCT = YES; 545 | VERSIONING_SYSTEM = "apple-generic"; 546 | VERSION_INFO_PREFIX = ""; 547 | }; 548 | name = Release; 549 | }; 550 | 33D2B50B219906C300D528EC /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 554 | APPLICATION_EXTENSION_API_ONLY = NO; 555 | CLANG_ENABLE_MODULES = YES; 556 | CODE_SIGN_IDENTITY = ""; 557 | CODE_SIGN_STYLE = Automatic; 558 | DEFINES_MODULE = YES; 559 | DYLIB_COMPATIBILITY_VERSION = 1; 560 | DYLIB_CURRENT_VERSION = 1; 561 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 562 | INFOPLIST_FILE = EmojiPicker/Info.plist; 563 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 564 | LD_RUNPATH_SEARCH_PATHS = ( 565 | "$(inherited)", 566 | "@executable_path/Frameworks", 567 | "@loader_path/Frameworks", 568 | ); 569 | PRODUCT_BUNDLE_IDENTIFIER = com.levantAJ.EmojiPicker; 570 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 571 | SKIP_INSTALL = YES; 572 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 573 | SWIFT_VERSION = 4.2; 574 | TARGETED_DEVICE_FAMILY = "1,2"; 575 | }; 576 | name = Debug; 577 | }; 578 | 33D2B50C219906C300D528EC /* Release */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 582 | APPLICATION_EXTENSION_API_ONLY = NO; 583 | CLANG_ENABLE_MODULES = YES; 584 | CODE_SIGN_IDENTITY = ""; 585 | CODE_SIGN_STYLE = Automatic; 586 | DEFINES_MODULE = YES; 587 | DYLIB_COMPATIBILITY_VERSION = 1; 588 | DYLIB_CURRENT_VERSION = 1; 589 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 590 | INFOPLIST_FILE = EmojiPicker/Info.plist; 591 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 592 | LD_RUNPATH_SEARCH_PATHS = ( 593 | "$(inherited)", 594 | "@executable_path/Frameworks", 595 | "@loader_path/Frameworks", 596 | ); 597 | PRODUCT_BUNDLE_IDENTIFIER = com.levantAJ.EmojiPicker; 598 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 599 | SKIP_INSTALL = YES; 600 | SWIFT_VERSION = 4.2; 601 | TARGETED_DEVICE_FAMILY = "1,2"; 602 | }; 603 | name = Release; 604 | }; 605 | 33D2B50E219906C300D528EC /* Debug */ = { 606 | isa = XCBuildConfiguration; 607 | buildSettings = { 608 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 609 | CODE_SIGN_STYLE = Automatic; 610 | INFOPLIST_FILE = EmojiPickerTests/Info.plist; 611 | LD_RUNPATH_SEARCH_PATHS = ( 612 | "$(inherited)", 613 | "@executable_path/Frameworks", 614 | "@loader_path/Frameworks", 615 | ); 616 | PRODUCT_BUNDLE_IDENTIFIER = com.levantAJ.EmojiPickerTests; 617 | PRODUCT_NAME = "$(TARGET_NAME)"; 618 | SWIFT_VERSION = 4.2; 619 | TARGETED_DEVICE_FAMILY = "1,2"; 620 | }; 621 | name = Debug; 622 | }; 623 | 33D2B50F219906C300D528EC /* Release */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 627 | CODE_SIGN_STYLE = Automatic; 628 | INFOPLIST_FILE = EmojiPickerTests/Info.plist; 629 | LD_RUNPATH_SEARCH_PATHS = ( 630 | "$(inherited)", 631 | "@executable_path/Frameworks", 632 | "@loader_path/Frameworks", 633 | ); 634 | PRODUCT_BUNDLE_IDENTIFIER = com.levantAJ.EmojiPickerTests; 635 | PRODUCT_NAME = "$(TARGET_NAME)"; 636 | SWIFT_VERSION = 4.2; 637 | TARGETED_DEVICE_FAMILY = "1,2"; 638 | }; 639 | name = Release; 640 | }; 641 | /* End XCBuildConfiguration section */ 642 | 643 | /* Begin XCConfigurationList section */ 644 | 33D2B4F0219906C300D528EC /* Build configuration list for PBXProject "EmojiPicker" */ = { 645 | isa = XCConfigurationList; 646 | buildConfigurations = ( 647 | 33D2B508219906C300D528EC /* Debug */, 648 | 33D2B509219906C300D528EC /* Release */, 649 | ); 650 | defaultConfigurationIsVisible = 0; 651 | defaultConfigurationName = Release; 652 | }; 653 | 33D2B50A219906C300D528EC /* Build configuration list for PBXNativeTarget "EmojiPicker" */ = { 654 | isa = XCConfigurationList; 655 | buildConfigurations = ( 656 | 33D2B50B219906C300D528EC /* Debug */, 657 | 33D2B50C219906C300D528EC /* Release */, 658 | ); 659 | defaultConfigurationIsVisible = 0; 660 | defaultConfigurationName = Release; 661 | }; 662 | 33D2B50D219906C300D528EC /* Build configuration list for PBXNativeTarget "EmojiPickerTests" */ = { 663 | isa = XCConfigurationList; 664 | buildConfigurations = ( 665 | 33D2B50E219906C300D528EC /* Debug */, 666 | 33D2B50F219906C300D528EC /* Release */, 667 | ); 668 | defaultConfigurationIsVisible = 0; 669 | defaultConfigurationName = Release; 670 | }; 671 | /* End XCConfigurationList section */ 672 | }; 673 | rootObject = 33D2B4ED219906C300D528EC /* Project object */; 674 | } 675 | -------------------------------------------------------------------------------- /EmojiPicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /EmojiPicker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /EmojiPicker.xcodeproj/xcshareddata/xcschemes/EmojiPicker.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /EmojiPicker.xcodeproj/xcshareddata/xcschemes/EmojiPickerTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /EmojiPicker/EmojiPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPicker.h 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for EmojiPicker. 12 | FOUNDATION_EXPORT double EmojiPickerVersionNumber; 13 | 14 | //! Project version string for EmojiPicker. 15 | FOUNDATION_EXPORT const unsigned char EmojiPickerVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /EmojiPicker/EmojiPicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPicker.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class EmojiPicker { 12 | public class var viewController: EmojiPickerViewController { 13 | let storyboard = UIStoryboard(name: "EmojiPicker", bundle: Bundle(for: EmojiPickerViewController.self)) 14 | let viewController = storyboard.instantiateInitialViewController() as! EmojiPickerViewController 15 | viewController.modalPresentationStyle = .overCurrentContext 16 | return viewController 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Extension/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | extension String { 10 | var localized: String { 11 | let language = UserDefaults.standard.string(forKey: Constant.CurrentLanguage.currentLanguageKey) 12 | var bundle = Bundle(for: EmojiPicker.self) 13 | if Constant.CurrentLanguage.language != language { 14 | if let path = bundle.path(forResource: language, ofType: "lproj") { 15 | bundle = Bundle(path: path)! 16 | } 17 | Constant.CurrentLanguage.language = language 18 | Constant.CurrentLanguage.bundle = bundle 19 | } else { 20 | bundle = Constant.CurrentLanguage.bundle 21 | } 22 | return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "") 23 | } 24 | } 25 | 26 | extension Constant { 27 | struct CurrentLanguage { 28 | static var language: String? = nil 29 | static var bundle = Bundle(for: EmojiPicker.self) 30 | static let currentLanguageKey = "com.levantAJ.EmojiPicker.currentLanguage" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Extension/UIButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButto.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 15/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton { 12 | func setTitle(_ title: String?, for state: UIControl.State, animated: Bool = true) { 13 | if animated { 14 | setTitle(title, for: state) 15 | } else { 16 | UIView.performWithoutAnimation { 17 | setTitle(title, for: state) 18 | layoutIfNeeded() 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Extension/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 13/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | public convenience init?(hexString: String, alpha: Float = 1.0) { 13 | var hex = hexString 14 | 15 | if hex.hasPrefix("#") { 16 | let subHex = Substring(hex) 17 | hex = String(subHex.suffix(from: subHex.index(hex.startIndex, offsetBy: 1))) 18 | } 19 | 20 | if hex.range(of: "(^[0-9A-Fa-f]{6}$)|(^[0-9A-Fa-f]{3}$)", options: .regularExpression) != nil { 21 | var subHex = Substring(hex) 22 | if hex.count == 3 { 23 | let redHex = subHex.prefix(upTo: subHex.index(hex.startIndex, offsetBy: 1)) 24 | let greenHex = String(subHex[subHex.index(hex.startIndex, offsetBy: 1).. Void)?) 14 | func hide() 15 | } 16 | 17 | final class EmojiPreviewer: UIView { 18 | @IBOutlet weak var singleEmojiWrapperView: UIView! 19 | @IBOutlet weak var singleEmojiImageView: UIImageView! 20 | @IBOutlet weak var singleEmojiLabel: UILabel! 21 | 22 | @IBOutlet weak var multipleEmojisWrapperView: UIView! 23 | @IBOutlet weak var multipleEmojisLeftImageView: UIImageView! 24 | @IBOutlet weak var multipleEmojisCenterLeftImageView: UIImageView! 25 | @IBOutlet weak var multipleEmojisCenterLeftImageViewWidthConstraint: NSLayoutConstraint! 26 | @IBOutlet weak var multipleEmojisAnchorImageView: UIImageView! 27 | @IBOutlet weak var multipleEmojisCenterRightImageView: UIImageView! 28 | @IBOutlet weak var multipleEmojisRightImageView: UIImageView! 29 | @IBOutlet weak var multipleEmojisDefaultButton: UIButton! 30 | @IBOutlet weak var multipleEmojisWhiteButton: UIButton! 31 | @IBOutlet weak var multipleEmojisYellowButton: UIButton! 32 | @IBOutlet weak var multipleEmojisLightButton: UIButton! 33 | @IBOutlet weak var multipleEmojisDarkButton: UIButton! 34 | @IBOutlet weak var multipleEmojisBlackButton: UIButton! 35 | var selectedButton: UIButton? 36 | lazy var vibrator: Vibratable = Vibrator() 37 | var emoji: Emoji! 38 | var completion: ((String) -> Void)? 39 | 40 | static let shared: EmojiPreviewer = { 41 | let nib = UINib(nibName: "EmojiPreviewer", bundle: Bundle(for: EmojiPreviewer.self)) 42 | let view = nib.instantiate(withOwner: nil, options: nil)[0] as! EmojiPreviewer 43 | view.backgroundColor = .clear 44 | view.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude) 45 | return view 46 | }() 47 | 48 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 49 | super.touchesEnded(touches, with: event) 50 | guard !(touches.first?.view is UIButton) else { return } 51 | hide() 52 | } 53 | } 54 | 55 | // MARK: - EmojiPreviewable 56 | 57 | extension EmojiPreviewer: EmojiPreviewable { 58 | func brief(sourceView: UIView, sourceRect: CGRect, emoji: Emoji, emojiFontSize: CGFloat, isDarkMode: Bool) { 59 | self.emoji = emoji 60 | completion = nil 61 | singleEmojiWrapperView.isHidden = false 62 | multipleEmojisWrapperView.isHidden = true 63 | setupView(for: emoji.emojis[0], sourceRect: sourceRect, emojiFontSize: emojiFontSize, isDarkMode: isDarkMode) 64 | 65 | sourceView.addSubview(self) 66 | } 67 | 68 | func preview(sourceView: UIView, sourceRect: CGRect, emoji: Emoji, emojiFontSize: CGFloat, isDarkMode: Bool, completion: ((String) -> Void)?) { 69 | self.emoji = emoji 70 | self.completion = completion 71 | guard emoji.emojis.count > 1 else { return } 72 | singleEmojiWrapperView.isHidden = true 73 | multipleEmojisWrapperView.isHidden = false 74 | setupView(for: emoji, sourceView: sourceView, sourceRect: sourceRect, emojiFontSize: emojiFontSize, isDarkMode: isDarkMode) 75 | 76 | sourceView.addSubview(self) 77 | } 78 | 79 | func hide() { 80 | removeFromSuperview() 81 | } 82 | } 83 | 84 | // MARK: - User Interactions 85 | 86 | extension EmojiPreviewer { 87 | @IBAction func multipleEmojisButtonTapped(_ button: UIButton) { 88 | switch button { 89 | case multipleEmojisDefaultButton: 90 | completion?(emoji.emojis[0]) 91 | case multipleEmojisWhiteButton: 92 | completion?(emoji.emojis[1]) 93 | case multipleEmojisYellowButton: 94 | completion?(emoji.emojis[2]) 95 | case multipleEmojisLightButton: 96 | completion?(emoji.emojis[3]) 97 | case multipleEmojisDarkButton: 98 | completion?(emoji.emojis[4]) 99 | case multipleEmojisBlackButton: 100 | completion?(emoji.emojis[5]) 101 | default: 102 | return 103 | } 104 | hide() 105 | } 106 | 107 | @IBAction func multipleEmojisButtonTouchDown(_ button: UIButton) { 108 | guard button != selectedButton else { return } 109 | vibrator.vibrate() 110 | selectedButton?.backgroundColor = .clear 111 | button.backgroundColor = button.tintColor 112 | selectedButton = button 113 | } 114 | } 115 | 116 | // MARK: - Privates 117 | 118 | extension EmojiPreviewer { 119 | private func setupView(for emoji: String, sourceRect: CGRect, emojiFontSize: CGFloat, isDarkMode: Bool) { 120 | let image = UIImage(named: isDarkMode ? "darkEmojiTag" : "lightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil)! 121 | singleEmojiImageView.image = image 122 | singleEmojiLabel.text = emoji 123 | singleEmojiLabel.font = UIFont.systemFont(ofSize: emojiFontSize) 124 | let width = Constant.EmojiCollectionViewCell.size.width + 37 125 | frame.size.height = width * image.size.height / image.size.width 126 | layoutIfNeeded() 127 | frame.size.width = singleEmojiWrapperView.frame.width 128 | frame.origin.x = sourceRect.midX - frame.width/2 129 | frame.origin.y = sourceRect.minY - frame.height + sourceRect.height + 9 130 | } 131 | 132 | private func setupView(for emoji: Emoji, sourceView: UIView, sourceRect: CGRect, emojiFontSize: CGFloat, isDarkMode: Bool) { 133 | multipleEmojisDefaultButton.titleLabel?.font = UIFont.systemFont(ofSize: emojiFontSize) 134 | multipleEmojisDefaultButton.backgroundColor = multipleEmojisDefaultButton.tintColor 135 | selectedButton = multipleEmojisDefaultButton 136 | multipleEmojisWhiteButton.titleLabel?.font = multipleEmojisDefaultButton.titleLabel?.font 137 | multipleEmojisWhiteButton.backgroundColor = .clear 138 | multipleEmojisYellowButton.titleLabel?.font = multipleEmojisDefaultButton.titleLabel?.font 139 | multipleEmojisYellowButton.backgroundColor = .clear 140 | multipleEmojisLightButton.titleLabel?.font = multipleEmojisDefaultButton.titleLabel?.font 141 | multipleEmojisLightButton.backgroundColor = .clear 142 | multipleEmojisDarkButton.titleLabel?.font = multipleEmojisDefaultButton.titleLabel?.font 143 | multipleEmojisDarkButton.backgroundColor = .clear 144 | multipleEmojisBlackButton.titleLabel?.font = multipleEmojisDefaultButton.titleLabel?.font 145 | multipleEmojisBlackButton.backgroundColor = .clear 146 | multipleEmojisDefaultButton.setTitle(emoji.emojis[0], for: .normal, animated: false) 147 | multipleEmojisWhiteButton.setTitle(emoji.emojis[1], for: .normal, animated: false) 148 | multipleEmojisYellowButton.setTitle(emoji.emojis[2], for: .normal, animated: false) 149 | multipleEmojisLightButton.setTitle(emoji.emojis[3], for: .normal, animated: false) 150 | multipleEmojisDarkButton.setTitle(emoji.emojis[4], for: .normal, animated: false) 151 | multipleEmojisBlackButton.setTitle(emoji.emojis[5], for: .normal, animated: false) 152 | 153 | multipleEmojisAnchorImageView.image = UIImage(named: isDarkMode ? "anchorDarkEmojiTag" : "anchorLightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil) 154 | multipleEmojisLeftImageView.image = UIImage(named: isDarkMode ? "leftDarkEmojiTag" : "leftLightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil) 155 | multipleEmojisRightImageView.image = UIImage(named: isDarkMode ? "rightDarkEmojiTag" : "rightLightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil) 156 | multipleEmojisCenterLeftImageView.image = UIImage(named: isDarkMode ? "centerDarkEmojiTag" : "centerLightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil) 157 | multipleEmojisCenterRightImageView.image = multipleEmojisCenterLeftImageView.image 158 | 159 | let image = UIImage(named: isDarkMode ? "darkEmojiTag" : "lightEmojiTag", in: Bundle(for: EmojiPreviewer.self), compatibleWith: nil)! 160 | let width = Constant.EmojiCollectionViewCell.size.width + 37 161 | frame.size.height = width * image.size.height / image.size.width 162 | multipleEmojisWrapperView.layoutIfNeeded() 163 | frame.size.width = multipleEmojisWrapperView.frame.width 164 | frame.origin.y = sourceRect.minY - frame.height + sourceRect.height + 9 165 | center.x = sourceRect.midX 166 | 167 | var factor: CGFloat = 0 168 | if sourceRect.minX <= multipleEmojisLeftImageView.frame.width + multipleEmojisAnchorImageView.frame.width/3 { 169 | factor = abs(frame.minX) - multipleEmojisLeftImageView.frame.width 170 | } else if frame.minX <= 0 { 171 | factor = abs(frame.minX) + multipleEmojisLeftImageView.frame.width 172 | } else if sourceView.frame.width - sourceRect.maxX <= multipleEmojisRightImageView.frame.width + multipleEmojisAnchorImageView.frame.width/3 { 173 | factor = sourceView.frame.width - frame.maxX + multipleEmojisRightImageView.frame.width 174 | } else if frame.maxX >= sourceView.frame.width { 175 | factor = sourceView.frame.width - frame.maxX 176 | } 177 | frame.origin.x = frame.origin.x + factor 178 | multipleEmojisCenterLeftImageViewWidthConstraint.constant = sourceRect.minX - frame.minX - multipleEmojisAnchorImageView.frame.width/2 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Utility/EmojiPreviewer.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 43 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Utility/UserDefaultsProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsProtocol.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol UserDefaultsProtocol { 12 | func data(forKey defaultName: String) -> Data? 13 | func set(_ value: Any?, forKey defaultName: String) 14 | func dictionary(forKey defaultName: String) -> [String : Any]? 15 | } 16 | 17 | // MARK: - UserDefaultsProtocol 18 | 19 | extension UserDefaults: UserDefaultsProtocol {} 20 | -------------------------------------------------------------------------------- /EmojiPicker/Engine/Utility/Vibrator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vibrator.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 13/11/18. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol Vibratable { 11 | func vibrate() 12 | } 13 | 14 | struct Vibrator {} 15 | 16 | // MARK: - Vibrating 17 | 18 | extension Vibrator: Vibratable { 19 | func vibrate() { 20 | if #available(iOS 10.0, *) { 21 | UIImpactFeedbackGenerator(style: .light).impactOccurred() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EmojiPicker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /EmojiPicker/Model/Category.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Category.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum EmojiGroup: String, Codable { 12 | case frequentlyUsed 13 | case smileysAndPeople 14 | case animalsAndNature 15 | case foodAndDrink 16 | case activity 17 | case travelAndPlaces 18 | case objects 19 | case symbols 20 | case flags 21 | 22 | init?(index: Int) { 23 | switch index { 24 | case 0: 25 | self = .frequentlyUsed 26 | case 1: 27 | self = .smileysAndPeople 28 | case 2: 29 | self = .animalsAndNature 30 | case 3: 31 | self = .foodAndDrink 32 | case 4: 33 | self = .activity 34 | case 5: 35 | self = .travelAndPlaces 36 | case 6: 37 | self = .objects 38 | case 7: 39 | self = .symbols 40 | case 8: 41 | self = .flags 42 | default: 43 | return nil 44 | } 45 | } 46 | 47 | var index: Int { 48 | switch self { 49 | case .frequentlyUsed: 50 | return 0 51 | case .smileysAndPeople: 52 | return 1 53 | case .animalsAndNature: 54 | return 2 55 | case .foodAndDrink: 56 | return 3 57 | case .activity: 58 | return 4 59 | case .travelAndPlaces: 60 | return 5 61 | case .objects: 62 | return 6 63 | case .symbols: 64 | return 7 65 | case .flags: 66 | return 8 67 | } 68 | } 69 | 70 | var name: String { 71 | switch self { 72 | case .frequentlyUsed: 73 | return "FREQUENTLY USED".localized 74 | case .smileysAndPeople: 75 | return "SMILEYS & PEOPLE".localized 76 | case .animalsAndNature: 77 | return "ANIMALS & NATURE".localized 78 | case .foodAndDrink: 79 | return "FOOD & DRINK".localized 80 | case .activity: 81 | return "ACTIVITY".localized 82 | case .travelAndPlaces: 83 | return "TRAVEL AND PLACES".localized 84 | case .objects: 85 | return "OBJECTS".localized 86 | case .symbols: 87 | return "SYMBOLS".localized 88 | case .flags: 89 | return "FLAGS".localized 90 | } 91 | } 92 | } 93 | 94 | struct Category: Codable { 95 | var emojis: [Emoji]! 96 | var type: EmojiGroup! 97 | 98 | enum CodingKeys: String, CodingKey { 99 | case emojis 100 | case type 101 | } 102 | 103 | init(from decoder: Decoder) throws { 104 | let values = try decoder.container(keyedBy: CodingKeys.self) 105 | emojis = try values.decode([[String]].self, forKey: .emojis).map { Emoji(emojis: $0) } 106 | type = try values.decode(EmojiGroup.self, forKey: .type) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /EmojiPicker/Model/Emoji.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Emoji.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 16/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Emoji: Codable { 12 | var emojis: [String]! 13 | var selectedEmoji: String? 14 | 15 | init(emojis: [String], selectedEmoji: String? = nil) { 16 | self.emojis = emojis 17 | self.selectedEmoji = selectedEmoji 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/activity.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "activity.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "activity@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "activity@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/activity.imageset/activity@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "anchorDarkEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "anchorDarkEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "anchorDarkEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorDarkEmojiTag.imageset/anchorDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "anchorLightEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "anchorLightEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "anchorLightEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/anchorLightEmojiTag.imageset/anchorLightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "animalsAndNature.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "animalsAndNature@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "animalsAndNature@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/animalsAndNature.imageset/animalsAndNature@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "centerDarkEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "centerDarkEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "centerDarkEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerDarkEmojiTag.imageset/centerDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "centerLightEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "centerLightEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "centerLightEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/centerLightEmojiTag.imageset/centerLightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "darkEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "darkEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "darkEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/darkEmojiTag.imageset/darkEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/flags.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "flags.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "flags@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "flags@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/flags.imageset/flags@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "foodAndDrink.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "foodAndDrink@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "foodAndDrink@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/foodAndDrink.imageset/foodAndDrink@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "frequentlyUsed.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "frequentlyUsed@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "frequentlyUsed@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/frequentlyUsed.imageset/frequentlyUsed@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "leftDarkEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "leftDarkEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "leftDarkEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftDarkEmojiTag.imageset/leftDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "leftLightEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "leftLightEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "leftLightEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/leftLightEmojiTag.imageset/leftLightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "lightEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "lightEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "lightEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/lightEmojiTag.imageset/lightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/objects.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "objects.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "objects@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "objects@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/objects.imageset/objects@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "rightDarkEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "rightDarkEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "rightDarkEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightDarkEmojiTag.imageset/rightDarkEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "rightLightEmojiTag.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "rightLightEmojiTag@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "rightLightEmojiTag@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/rightLightEmojiTag.imageset/rightLightEmojiTag@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "smileysAndPeople.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "smileysAndPeople@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "smileysAndPeople@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/smileysAndPeople.imageset/smileysAndPeople@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/symbols.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "symbols.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "symbols@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "symbols@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/symbols.imageset/symbols@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "travelAndPlaces.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "travelAndPlaces@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "travelAndPlaces@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces@2x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/EmojiPicker/Resource/Assets.xcassets/travelAndPlaces.imageset/travelAndPlaces@3x.png -------------------------------------------------------------------------------- /EmojiPicker/Resource/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | EmojiPicker 4 | 5 | Created by levantAJ on 13/11/18. 6 | Copyright © 2018 levantAJ. All rights reserved. 7 | */ 8 | "FREQUENTLY USED" = "FREQUENTLY USED"; 9 | "SMILEYS & PEOPLE" = "SMILEYS & PEOPLE"; 10 | "ANIMALS & NATURE" = "ANIMALS & NATURE"; 11 | "FOOD & DRINK" = "FOOD & DRINK"; 12 | "ACTIVITY" = "ACTIVITY"; 13 | "TRAVEL AND PLACES" = "TRAVEL AND PLACES"; 14 | "OBJECTS" = "OBJECTS"; 15 | "SYMBOLS" = "SYMBOLS"; 16 | "FLAGS" = "FLAGS"; 17 | -------------------------------------------------------------------------------- /EmojiPicker/Resource/vi.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | EmojiPicker 4 | 5 | Created by levantAJ on 13/11/18. 6 | Copyright © 2018 levantAJ. All rights reserved. 7 | */ 8 | "FREQUENTLY USED" = "GẦN ĐÂY"; 9 | "SMILEYS & PEOPLE" = "MẶT CƯỜI & NGƯỜI"; 10 | "ANIMALS & NATURE" = "ĐỘNG VẬT & THIÊN NHIÊN"; 11 | "FOOD & DRINK" = "ĐỒ ĂN THỨC UỐNG"; 12 | "ACTIVITY" = "HOẠT ĐỘNG"; 13 | "TRAVEL AND PLACES" = "DU LỊCH & ĐỊA ĐIỂM"; 14 | "OBJECTS" = "VẬT THỂ"; 15 | "SYMBOLS" = "BIỂU TƯỢNG"; 16 | "FLAGS" = "CỜ"; 17 | -------------------------------------------------------------------------------- /EmojiPicker/Storyboard/EmojiPicker.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /EmojiPicker/Storyboard/EmojiPopover.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPicker/EmojiPickerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPickerViewController.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 15/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol EmojiPickerViewControllerDelegate: class { 12 | func emojiPickerViewController(_ controller: EmojiPickerViewController, didSelect emoji: String) 13 | } 14 | 15 | open class EmojiPickerViewController: UIViewController { 16 | open var sourceRect: CGRect = .zero 17 | open var permittedArrowDirections: UIPopoverArrowDirection = .any 18 | open var emojiFontSize: CGFloat = 29 19 | open var backgroundColor: UIColor? = UIColor.white.withAlphaComponent(0.5) 20 | open var darkModeBackgroundColor: UIColor? = UIColor.black.withAlphaComponent(0.5) 21 | open var isDarkMode = false 22 | open var language: String? 23 | open var dismissAfterSelected = false 24 | open var size: CGSize = CGSize(width: 200, height: 300) 25 | open weak var delegate: EmojiPickerViewControllerDelegate? 26 | lazy var emojiPreviewer: EmojiPreviewable = EmojiPreviewer.shared 27 | var emojiPopoverVC: EmojiPopoverViewController! 28 | 29 | open override func viewWillAppear(_ animated: Bool) { 30 | super.viewWillAppear(animated) 31 | let storyboard = UIStoryboard(name: "EmojiPopover", bundle: Bundle(for: EmojiPopoverViewController.self)) 32 | emojiPopoverVC = storyboard.instantiateInitialViewController() as? EmojiPopoverViewController 33 | emojiPopoverVC.delegate = self 34 | emojiPopoverVC.sourceView = view 35 | emojiPopoverVC.sourceRect = sourceRect 36 | emojiPopoverVC.delegate = self 37 | emojiPopoverVC.isDarkMode = isDarkMode 38 | emojiPopoverVC.language = language 39 | emojiPopoverVC.emojiFontSize = emojiFontSize 40 | emojiPopoverVC.dismissAfterSelected = dismissAfterSelected 41 | emojiPopoverVC.darkModeBackgroundColor = darkModeBackgroundColor 42 | emojiPopoverVC.backgroundColor = backgroundColor 43 | emojiPopoverVC.permittedArrowDirections = permittedArrowDirections 44 | emojiPopoverVC.preferredContentSize = size 45 | present(emojiPopoverVC, animated: true, completion: nil) 46 | } 47 | } 48 | 49 | // MARK: - EmojiPickerContentViewControllerDelegate 50 | 51 | extension EmojiPickerViewController: EmojiPopoverViewControllerDelegate { 52 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, didSelect emoji: Emoji) { 53 | emojiPreviewer.hide() 54 | delegate?.emojiPickerViewController(self, didSelect: emoji.selectedEmoji ?? emoji.emojis.first!) 55 | } 56 | 57 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, brief emoji: Emoji, sourceView: UIView) { 58 | let sourceRect = sourceView.convert(sourceView.bounds, to: view) 59 | emojiPreviewer.brief(sourceView: view.window!, sourceRect: sourceRect, emoji: emoji, emojiFontSize: emojiFontSize, isDarkMode: isDarkMode) 60 | } 61 | 62 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, preview emoji: Emoji, sourceView: UIView) { 63 | let sourceRect = sourceView.convert(sourceView.bounds, to: view) 64 | emojiPreviewer.preview(sourceView: view.window!, sourceRect: sourceRect, emoji: emoji, emojiFontSize: emojiFontSize, isDarkMode: isDarkMode) { [weak self] selectedEmoji in 65 | guard let strongSelf = self else { return } 66 | var emoji = emoji 67 | emoji.selectedEmoji = selectedEmoji 68 | strongSelf.emojiPopoverVC.select(emoji: emoji) 69 | strongSelf.delegate?.emojiPickerViewController(strongSelf, didSelect: selectedEmoji) 70 | } 71 | } 72 | 73 | func emojiPickerViewControllerHideDeselectEmoji(_ controller: EmojiPopoverViewController) { 74 | emojiPreviewer.hide() 75 | } 76 | 77 | func emojiPickerViewControllerDidDimiss(_ controller: EmojiPopoverViewController) { 78 | emojiPreviewer.hide() 79 | dismiss(animated: true, completion: nil) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/EmojiCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiCollectionViewCell.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EmojiCollectionViewCellDelegate: class { 12 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, brief emoji: Emoji) 13 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, select emoji: Emoji) 14 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, deselect emoji: Emoji) 15 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, preview emoji: Emoji) 16 | } 17 | 18 | final class EmojiCollectionViewCell: UICollectionViewCell { 19 | @IBOutlet weak var emojiButton: UIButton! 20 | weak var delegate: EmojiCollectionViewCellDelegate? 21 | lazy var vibrator: Vibratable = Vibrator() 22 | var emoji: Emoji! { 23 | didSet { 24 | let title = emoji.selectedEmoji ?? emoji.emojis.first 25 | emojiButton.setTitle(title, for: .normal, animated: false) 26 | } 27 | } 28 | var emojiFontSize: CGFloat = 29 { 29 | didSet { 30 | emojiButton.titleLabel?.font = UIFont.systemFont(ofSize: emojiFontSize) 31 | } 32 | } 33 | 34 | override func awakeFromNib() { 35 | super.awakeFromNib() 36 | setupViews() 37 | } 38 | } 39 | 40 | // MARK: - User Interactions 41 | 42 | extension EmojiCollectionViewCell { 43 | @IBAction func emojiButtonTouchDown(_ button: UIButton) { 44 | delegate?.emojiCollectionViewCell(self, brief: emoji) 45 | } 46 | 47 | @IBAction func emojiButtonTouchUpInside(_ button: UIButton) { 48 | delegate?.emojiCollectionViewCell(self, select: emoji) 49 | } 50 | 51 | @IBAction func emojiButtonTouchUpOutside(_ button: UIButton) { 52 | delegate?.emojiCollectionViewCell(self, deselect: emoji) 53 | } 54 | 55 | @objc private func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) { 56 | if emoji.emojis.count == 1 { 57 | if longPressGestureRecognizer.state == .ended { 58 | delegate?.emojiCollectionViewCell(self, select: emoji) 59 | } 60 | } else { 61 | if longPressGestureRecognizer.state == .began { 62 | vibrator.vibrate() 63 | delegate?.emojiCollectionViewCell(self, preview: emoji) 64 | } 65 | } 66 | } 67 | 68 | } 69 | 70 | // MARK: - Privates 71 | 72 | extension EmojiCollectionViewCell { 73 | private func setupViews() { 74 | let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPress(_:))) 75 | emojiButton.addGestureRecognizer(longPressGestureRecognizer) 76 | } 77 | } 78 | 79 | extension Constant { 80 | struct EmojiCollectionViewCell { 81 | static let identifier = "EmojiCollectionViewCell" 82 | static let size = CGSize(width: 37, height: 37) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/EmojiCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/EmojiHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiHeaderView.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class EmojiHeaderView: UICollectionReusableView { 12 | @IBOutlet weak var titleLabel: UILabel! 13 | 14 | var title: String? { 15 | didSet { 16 | titleLabel.text = title 17 | } 18 | } 19 | } 20 | 21 | extension Constant { 22 | struct EmojiHeaderView { 23 | static let identifier = "EmojiHeaderView" 24 | static let height: CGFloat = 32 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/EmojiHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/EmojiPopoverViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPickerContentViewController.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EmojiPopoverViewControllerDelegate: class { 12 | func emojiPickerViewControllerDidDimiss(_ controller: EmojiPopoverViewController) 13 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, brief emoji: Emoji, sourceView: UIView) 14 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, preview emoji: Emoji, sourceView: UIView) 15 | func emojiPickerViewController(_ controller: EmojiPopoverViewController, didSelect emoji: Emoji) 16 | func emojiPickerViewControllerHideDeselectEmoji(_ controller: EmojiPopoverViewController) 17 | } 18 | 19 | final class EmojiPopoverViewController: UIViewController { 20 | var sourceRect: CGRect = .zero { 21 | didSet { 22 | popoverPresentationController?.sourceRect = sourceRect 23 | } 24 | } 25 | var sourceView: UIView? { 26 | didSet { 27 | popoverPresentationController?.sourceView = sourceView 28 | } 29 | } 30 | var permittedArrowDirections: UIPopoverArrowDirection = .any { 31 | didSet { 32 | popoverPresentationController?.permittedArrowDirections = permittedArrowDirections 33 | } 34 | } 35 | var emojiFontSize: CGFloat = 29 { 36 | didSet { 37 | emojisCollectionView?.reloadData() 38 | } 39 | } 40 | var backgroundColor: UIColor? = UIColor.white.withAlphaComponent(0.5) { 41 | didSet { 42 | changeDarkModeStyle() 43 | } 44 | } 45 | var darkModeBackgroundColor: UIColor? = UIColor.black.withAlphaComponent(0.5) { 46 | didSet { 47 | changeDarkModeStyle() 48 | } 49 | } 50 | var isDarkMode = false { 51 | didSet { 52 | changeDarkModeStyle() 53 | } 54 | } 55 | var language: String? { 56 | didSet { 57 | UserDefaults.standard.set(language, forKey: Constant.CurrentLanguage.currentLanguageKey) 58 | } 59 | } 60 | var dismissAfterSelected = false 61 | weak var delegate: EmojiPopoverViewControllerDelegate? 62 | 63 | @IBOutlet weak var emojisCollectionView: UICollectionView! 64 | @IBOutlet weak var groupsCollectionView: UICollectionView! 65 | @IBOutlet weak var bottomVisualEffectView: UIVisualEffectView! 66 | @IBOutlet weak var groupTopLineView: UIView! 67 | var selectedGroupCell: GroupCollectionViewCell? 68 | lazy var viewModel: EmojiPickerViewModelProtocol = EmojiPickerViewModel(userDefaults: UserDefaults.standard) 69 | lazy var vibrator: Vibratable = Vibrator() 70 | 71 | override func awakeFromNib() { 72 | super.awakeFromNib() 73 | modalPresentationStyle = .popover 74 | popoverPresentationController?.permittedArrowDirections = permittedArrowDirections 75 | popoverPresentationController?.delegate = self 76 | } 77 | 78 | override func viewDidLoad() { 79 | super.viewDidLoad() 80 | setupViews() 81 | } 82 | 83 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { 84 | return .none 85 | } 86 | 87 | func select(emoji: Emoji) { 88 | viewModel.select(emoji: emoji) 89 | emojisCollectionView.reloadSections(IndexSet(integer: EmojiGroup.frequentlyUsed.index)) 90 | guard let indexPath = viewModel.indexPath(of: emoji) else { return } 91 | emojisCollectionView.reloadItems(at: [indexPath]) 92 | } 93 | } 94 | 95 | // MARK: - UICollectionViewDataSource 96 | 97 | extension EmojiPopoverViewController: UIPopoverPresentationControllerDelegate { 98 | func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) { 99 | delegate?.emojiPickerViewControllerDidDimiss(self) 100 | } 101 | } 102 | 103 | // MARK: - UICollectionViewDataSource 104 | 105 | extension EmojiPopoverViewController: UICollectionViewDataSource { 106 | func numberOfSections(in collectionView: UICollectionView) -> Int { 107 | if collectionView == emojisCollectionView { 108 | return viewModel.numberOfSections 109 | } 110 | return 1 111 | } 112 | 113 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 114 | if collectionView == emojisCollectionView { 115 | return viewModel.numberOfEmojis(section: section) 116 | } 117 | return viewModel.numberOfSections 118 | } 119 | 120 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 121 | if collectionView == emojisCollectionView { 122 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constant.EmojiCollectionViewCell.identifier, for: indexPath) as! EmojiCollectionViewCell 123 | cell.delegate = self 124 | cell.emojiFontSize = emojiFontSize 125 | if let emoji = viewModel.emoji(at: indexPath) { 126 | cell.emoji = emoji 127 | } 128 | return cell 129 | } 130 | 131 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constant.GroupCollectionViewCell.identifier, for: indexPath) as! GroupCollectionViewCell 132 | cell.indexPath = indexPath 133 | cell.delegate = self 134 | cell.isDarkMode = isDarkMode 135 | if let group = EmojiGroup(index: indexPath.item) { 136 | cell.image = UIImage(named: group.rawValue, in: Bundle(for: GroupCollectionViewCell.self), compatibleWith: nil) 137 | } 138 | if selectedGroupCell == nil { 139 | selectedGroupCell = cell 140 | selectedGroupCell?.isSelected = true 141 | } 142 | return cell 143 | } 144 | } 145 | 146 | // MARK: - UICollectionViewDelegate, UICollectionViewDelegateFlowLayout 147 | 148 | extension EmojiPopoverViewController: UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { 149 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 150 | if collectionView == emojisCollectionView { 151 | return Constant.EmojiCollectionViewCell.size 152 | } 153 | return CGSize(width: max(collectionView.frame.width/CGFloat(viewModel.numberOfSections), 32), height: collectionView.frame.height) 154 | } 155 | 156 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { 157 | if collectionView == emojisCollectionView { 158 | let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: Constant.EmojiHeaderView.identifier, for: indexPath) as! EmojiHeaderView 159 | if let group = EmojiGroup(index: indexPath.section) { 160 | headerView.title = group.name 161 | } 162 | return headerView 163 | } 164 | let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: Constant.GroupHeaderView.identifier, for: indexPath) 165 | return headerView 166 | } 167 | } 168 | 169 | // MARK: - UIScrollViewDelegate 170 | 171 | extension EmojiPopoverViewController { 172 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 173 | selectCurrentGroupCell() 174 | } 175 | 176 | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 177 | selectCurrentGroupCell() 178 | } 179 | 180 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 181 | delegate?.emojiPickerViewControllerHideDeselectEmoji(self) 182 | } 183 | } 184 | 185 | // MARK: - GroupCollectionViewCellDelegate 186 | 187 | extension EmojiPopoverViewController: EmojiCollectionViewCellDelegate { 188 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, brief emoji: Emoji) { 189 | delegate?.emojiPickerViewController(self, brief: emoji, sourceView: cell) 190 | } 191 | 192 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, select emoji: Emoji) { 193 | delegate?.emojiPickerViewController(self, didSelect: emoji) 194 | viewModel.select(emoji: emoji) 195 | if dismissAfterSelected { 196 | dismiss(animated: true) { [weak self] in 197 | guard let strongSelf = self else { return } 198 | strongSelf.delegate?.emojiPickerViewControllerDidDimiss(strongSelf) 199 | } 200 | } else if viewModel.indexPath(of: emoji) != nil { 201 | emojisCollectionView.reloadSections(IndexSet(integer: EmojiGroup.frequentlyUsed.index)) 202 | } 203 | } 204 | 205 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, deselect emoji: Emoji) { 206 | delegate?.emojiPickerViewControllerHideDeselectEmoji(self) 207 | } 208 | 209 | func emojiCollectionViewCell(_ cell: EmojiCollectionViewCell, preview emoji: Emoji) { 210 | delegate?.emojiPickerViewController(self, preview: emoji, sourceView: cell) 211 | } 212 | } 213 | 214 | // MARK: - GroupCollectionViewCellDelegate 215 | 216 | extension EmojiPopoverViewController: GroupCollectionViewCellDelegate { 217 | func groupCollectionViewCell(_ cell: GroupCollectionViewCell, didSelect indexPath: IndexPath) { 218 | selectedGroupCell?.isSelected = false 219 | selectedGroupCell = cell 220 | selectedGroupCell?.isSelected = true 221 | if indexPath.item == 0 { 222 | emojisCollectionView.scrollRectToVisible(CGRect(origin: .zero, size: CGSize(width: 1, height: 1)), animated: true) 223 | } else if let attributes = emojisCollectionView.collectionViewLayout.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: indexPath.item)) { 224 | emojisCollectionView.setContentOffset(CGPoint(x: 0, y: attributes.frame.origin.y - emojisCollectionView.contentInset.top), animated: true) 225 | } 226 | } 227 | } 228 | 229 | // MARK: - Privates 230 | 231 | extension EmojiPopoverViewController { 232 | private func setupViews() { 233 | emojisCollectionView.delegate = self 234 | emojisCollectionView.dataSource = self 235 | groupsCollectionView.delegate = self 236 | groupsCollectionView.dataSource = self 237 | 238 | var nib = UINib(nibName: Constant.EmojiCollectionViewCell.identifier, bundle: Bundle(for: EmojiCollectionViewCell.self)) 239 | emojisCollectionView.register(nib, forCellWithReuseIdentifier: Constant.EmojiCollectionViewCell.identifier) 240 | nib = UINib(nibName: Constant.EmojiHeaderView.identifier, bundle: Bundle(for: EmojiHeaderView.self)) 241 | emojisCollectionView.register(nib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: Constant.EmojiHeaderView.identifier) 242 | var layout = emojisCollectionView.collectionViewLayout as? UICollectionViewFlowLayout 243 | layout?.headerReferenceSize = CGSize(width: emojisCollectionView.frame.width, height: Constant.EmojiHeaderView.height) 244 | 245 | nib = UINib(nibName: Constant.GroupCollectionViewCell.identifier, bundle: Bundle(for: GroupCollectionViewCell.self)) 246 | groupsCollectionView.register(nib, forCellWithReuseIdentifier: Constant.GroupCollectionViewCell.identifier) 247 | nib = UINib(nibName: Constant.GroupHeaderView.identifier, bundle: Bundle(for: GroupHeaderView.self)) 248 | groupsCollectionView.register(nib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: Constant.GroupHeaderView.identifier) 249 | layout = groupsCollectionView.collectionViewLayout as? UICollectionViewFlowLayout 250 | layout?.headerReferenceSize = CGSize(width: 0, height: 0) 251 | 252 | changeDarkModeStyle() 253 | } 254 | 255 | private func changeDarkModeStyle() { 256 | popoverPresentationController?.backgroundColor = isDarkMode ? darkModeBackgroundColor : backgroundColor 257 | bottomVisualEffectView?.effect = UIBlurEffect(style: isDarkMode ? .dark : .light) 258 | groupTopLineView?.backgroundColor = UIColor(hexString: isDarkMode ? "#3d3d3d" : "#9d9d9d")?.withAlphaComponent(0.3) 259 | } 260 | 261 | private func selectCurrentGroupCell() { 262 | guard let emojiCell = emojisCollectionView.visibleCells.first, 263 | let indexPath = emojisCollectionView.indexPath(for: emojiCell), 264 | let groupCell = groupsCollectionView.cellForItem(at: IndexPath(item: indexPath.section, section: 0)) as? GroupCollectionViewCell else { return } 265 | selectedGroupCell?.isSelected = false 266 | selectedGroupCell = groupCell 267 | selectedGroupCell?.isSelected = true 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/GroupCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupCollectionViewCell.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol GroupCollectionViewCellDelegate: class { 12 | func groupCollectionViewCell(_ cell: GroupCollectionViewCell, didSelect indexPath: IndexPath) 13 | } 14 | 15 | final class GroupCollectionViewCell: UICollectionViewCell { 16 | @IBOutlet weak var groupButton: UIButton! 17 | var indexPath: IndexPath! 18 | weak var delegate: GroupCollectionViewCellDelegate? 19 | lazy var vibrator: Vibratable = Vibrator() 20 | 21 | var image: UIImage? { 22 | didSet { 23 | groupButton.tintColor = .darkGray 24 | groupButton.imageView?.contentMode = .scaleAspectFit 25 | groupButton.setImage(image, for: .normal) 26 | } 27 | } 28 | 29 | var isDarkMode = false { 30 | didSet { 31 | changeDarkModeStyle() 32 | } 33 | } 34 | 35 | override var isSelected: Bool { 36 | didSet { 37 | changeDarkModeStyle() 38 | } 39 | } 40 | 41 | override func layoutSubviews() { 42 | super.layoutSubviews() 43 | groupButton.layer.cornerRadius = groupButton.frame.width/2 44 | } 45 | 46 | override func prepareForReuse() { 47 | super.prepareForReuse() 48 | groupButton.backgroundColor = .clear 49 | } 50 | } 51 | 52 | // MARK: - User Interactions 53 | 54 | extension GroupCollectionViewCell { 55 | @IBAction func groupButtonTapped(_ button: UIButton) { 56 | isSelected = true 57 | vibrator.vibrate() 58 | delegate?.groupCollectionViewCell(self, didSelect: indexPath) 59 | } 60 | } 61 | 62 | // MARK: - Privates 63 | 64 | extension GroupCollectionViewCell { 65 | private func changeDarkModeStyle() { 66 | groupButton.backgroundColor = isSelected ? (isDarkMode ? UIColor.black : UIColor.lightGray.withAlphaComponent(0.5)) : .clear 67 | } 68 | } 69 | 70 | extension Constant { 71 | struct GroupCollectionViewCell { 72 | static let identifier = "GroupCollectionViewCell" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/GroupCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/GroupHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupHeaderView.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class GroupHeaderView: UICollectionReusableView {} 12 | 13 | extension Constant { 14 | struct GroupHeaderView { 15 | static let identifier = "GroupHeaderView" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EmojiPicker/View/EmojiPopover/GroupHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /EmojiPicker/ViewModel/EmojiPickerViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPickerViewModel.swift 3 | // EmojiPicker 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol EmojiPickerViewModelProtocol { 12 | var numberOfSections: Int { get } 13 | func numberOfEmojis(section: Int) -> Int 14 | func emoji(at indexPath: IndexPath) -> Emoji? 15 | func indexPath(of emoji: Emoji) -> IndexPath? 16 | func select(emoji: Emoji) 17 | } 18 | 19 | final class EmojiPickerViewModel { 20 | var emojis: [Int: [Emoji]] = [:] 21 | let userDefaults: UserDefaultsProtocol 22 | 23 | init(userDefaults: UserDefaultsProtocol) { 24 | self.userDefaults = userDefaults 25 | if let data = userDefaults.data(forKey: Constant.EmojiPickerViewModel.frequentlyUsed) { 26 | let frequentlyUsedEmojis = try! JSONDecoder().decode([Emoji].self, from: data) 27 | self.emojis[EmojiGroup.frequentlyUsed.index] = frequentlyUsedEmojis 28 | } else { 29 | self.emojis[EmojiGroup.frequentlyUsed.index] = [] 30 | } 31 | let systemVersion = UIDevice.current.systemVersion 32 | let path: String 33 | if systemVersion.compare("10", options: .numeric) == .orderedAscending { 34 | path = Bundle(for: EmojiPickerViewModel.self).path(forResource: "emojis9.1", ofType: "json")! 35 | } else if systemVersion.compare("12", options: .numeric) == .orderedAscending { 36 | path = Bundle(for: EmojiPickerViewModel.self).path(forResource: "emojis11.0.1", ofType: "json")! 37 | } else { 38 | path = Bundle(for: EmojiPickerViewModel.self).path(forResource: "emojis", ofType: "json")! 39 | } 40 | let data = try! Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped) 41 | let categories = try! JSONDecoder().decode([Category].self, from: data) 42 | 43 | let selectedEmojis = userDefaults.dictionary(forKey: Constant.EmojiPickerViewModel.selectedEmojis) as? [String: String] 44 | for var category in categories { 45 | if let selectedEmojis = selectedEmojis { 46 | for (index, emoji) in category.emojis.enumerated() { 47 | category.emojis[index].selectedEmoji = selectedEmojis[emoji.emojis.first!] 48 | } 49 | } 50 | self.emojis[category.type.index] = category.emojis 51 | } 52 | } 53 | } 54 | 55 | // MARK: - EmojiPickerViewModelProtocol 56 | 57 | extension EmojiPickerViewModel: EmojiPickerViewModelProtocol { 58 | var numberOfSections: Int { 59 | return emojis.count 60 | } 61 | 62 | func numberOfEmojis(section: Int) -> Int { 63 | guard let type = EmojiGroup(index: section) else { return 0 } 64 | return emojis[type.index]?.count ?? 0 65 | } 66 | 67 | func emoji(at indexPath: IndexPath) -> Emoji? { 68 | guard let type = EmojiGroup(index: indexPath.section) else { return nil } 69 | return emojis[type.index]?[indexPath.item] 70 | } 71 | 72 | func select(emoji: Emoji) { 73 | updateFrequentlyUsed(emoji: emoji) 74 | updateSelectedEmoji(emoji) 75 | 76 | for item in emojis { 77 | guard item.key != EmojiGroup.frequentlyUsed.index, 78 | let index = item.value.firstIndex(where: { $0.emojis == emoji.emojis }) else { continue } 79 | emojis[item.key]?[index] = emoji 80 | break 81 | } 82 | } 83 | 84 | func indexPath(of emoji: Emoji) -> IndexPath? { 85 | for item in emojis { 86 | guard item.key != EmojiGroup.frequentlyUsed.index, 87 | let index = item.value.firstIndex(where: { $0.emojis == emoji.emojis }) else { continue } 88 | return IndexPath(item: index, section: item.key) 89 | } 90 | return nil 91 | } 92 | } 93 | 94 | // MARK: - Privates 95 | 96 | extension EmojiPickerViewModel { 97 | private func updateFrequentlyUsed(emoji: Emoji) { 98 | var frequentlyUsedEmojis: [Emoji] = [] 99 | if let data = userDefaults.data(forKey: Constant.EmojiPickerViewModel.frequentlyUsed) { 100 | frequentlyUsedEmojis = try! JSONDecoder().decode([Emoji].self, from: data) 101 | } 102 | if let index = frequentlyUsedEmojis.firstIndex(where: { $0.emojis == emoji.emojis }) { 103 | frequentlyUsedEmojis.remove(at: index) 104 | } 105 | frequentlyUsedEmojis = [emoji] + frequentlyUsedEmojis 106 | frequentlyUsedEmojis = Array(frequentlyUsedEmojis.prefix(upTo: min(frequentlyUsedEmojis.count, 30))) 107 | emojis[EmojiGroup.frequentlyUsed.index] = frequentlyUsedEmojis 108 | let data = try! JSONEncoder().encode(frequentlyUsedEmojis) 109 | userDefaults.set(data, forKey: Constant.EmojiPickerViewModel.frequentlyUsed) 110 | } 111 | 112 | private func updateSelectedEmoji(_ emoji: Emoji) { 113 | guard emoji.selectedEmoji != emoji.emojis.first else { return } 114 | var selectedEmojis = userDefaults.dictionary(forKey: Constant.EmojiPickerViewModel.selectedEmojis) as? [String: String] ?? [:] 115 | selectedEmojis[emoji.emojis.first!] = emoji.selectedEmoji 116 | userDefaults.set(selectedEmojis, forKey: Constant.EmojiPickerViewModel.selectedEmojis) 117 | } 118 | } 119 | 120 | struct Constant { 121 | struct EmojiPickerViewModel { 122 | static let frequentlyUsed = "com.levantAJ.EmojiPicker.frequentlyUsed" 123 | static let selectedEmojis = "com.levantAJ.EmojiPicker.selectedEmojis" 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /EmojiPickerTests/EmojiPickerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiPickerTests.swift 3 | // EmojiPickerTests 4 | // 5 | // Created by levantAJ on 12/11/18. 6 | // Copyright © 2018 levantAJ. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import EmojiPicker 11 | 12 | class EmojiPickerTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /EmojiPickerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 levantAJ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [![CocoaPods](https://img.shields.io/cocoapods/p/EmojiPicker.svg)](https://cocoapods.org/pods/EmojiPicker) 6 | [![CocoaPods](https://img.shields.io/cocoapods/v/EmojiPicker.svg)](http://cocoapods.org/pods/EmojiPicker) 7 | [![Pod License](https://cocoapod-badges.herokuapp.com/l/EmojiPicker/badge.png)](https://www.apache.org/licenses/LICENSE-2.0.html) 8 | [![Build Status](https://travis-ci.org/levantAJ/EmojiPicker.svg?branch=master)](https://travis-ci.org/levantAJ/EmojiPicker) 9 | 10 | # EmojiPicker 11 | This library to show a popover to pick emojis for iOS 12 | 13 |

14 | 15 | 16 |

17 | 18 | ## Requirements 19 | 20 | - iOS 9.0 or later 21 | - Xcode 9.0 or later 22 | 23 | ## Installation 24 | There is a way to use EmojiPicker in your project: 25 | 26 | - using CocoaPods 27 | 28 | ### Installation with CocoaPods 29 | 30 | ``` 31 | pod 'EmojiPicker', '1.1' 32 | ``` 33 | ### Build Project 34 | 35 | At this point your workspace should build without error. If you are having problem, post to the Issue and the 36 | community can help you solve it. 37 | 38 | ## How To Use 39 | 40 | ```swift 41 | import EmojiPicker 42 | 43 | let emojiPickerVC = EmojiPicker.viewController 44 | emojiPickerVC.sourceView = view 45 | emojiPickerVC.sourceRect = targetView.frame 46 | present(emojiPickerVC, animated: true, completion: nil) 47 | ``` 48 | 49 | - Delegate [EmojiPickerViewControllerDelegate](https://github.com/levantAJ/EmojiPicker/blob/master/EmojiPicker/View/EmojiPickerViewController.swift) 50 | ```swift 51 | emojiPickerVC.delegate = self 52 | ``` 53 | 54 | - Change size: 55 | ```swift 56 | emojiPickerVC.size = CGSize(width: 300, height: 400) 57 | ``` 58 | 59 | - Change Emojis font size, default is `29`: 60 | ```swift 61 | emojiPickerVC.emojiFontSize = 29 62 | ``` 63 | 64 | - Dismiss dismiss picker after select an emoji, default is `false`: 65 | ```swift 66 | emojiPickerVC.dismissAfterSelected = false 67 | ``` 68 | 69 | - Apply dark mode, default is `false` 70 | ```swift 71 | emojiPickerVC.isDarkMode = false 72 | ``` 73 | 74 | - Background color in light mode, used when `isDarkMode` is `false`, default is `UIColor.white.withAlphaComponent(0.5)` 75 | ```swift 76 | emojiPickerVC.backgroundColor = UIColor.white.withAlphaComponent(0.5) 77 | ``` 78 | 79 | - Background color in dark mode, used when `isDarkMode` is `true`, default is `UIColor.black.withAlphaComponent(0.5)` 80 | ```swift 81 | emojiPickerVC.darkModeBackgroundColor = UIColor.white.withAlphaComponent(0.5) 82 | ``` 83 | 84 | - Change language, default is `nil` as system language, now supported languages: `English`, `Vietnamese` 85 | 86 | ```swift 87 | emojiPickerVC.language = "en" 88 | ``` 89 | 90 | - Change permitted arrow directions [UIPopoverArrowDirection](https://developer.apple.com/documentation/uikit/uipopoverarrowdirection), default is `any` 91 | 92 | ```swift 93 | emojiPickerVC.permittedArrowDirections = .any 94 | ``` 95 | 96 | ## Author 97 | - [Tai Le](https://github.com/levantAJ) 98 | 99 | ## Communication 100 | - If you **found a bug**, open an issue. 101 | - If you **have a feature request**, open an issue. 102 | - If you **want to contribute**, submit a pull request. 103 | 104 | ## Licenses 105 | 106 | All source code is licensed under the [MIT License](https://raw.githubusercontent.com/levantAJ/EmojiPicker/master/LICENSE). 107 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/icon.png -------------------------------------------------------------------------------- /ipad-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/ipad-example.gif -------------------------------------------------------------------------------- /iphone-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/levantAJ/EmojiPicker/f17ab1808eb9ba0884945c8aef046620362a9acf/iphone-example.gif --------------------------------------------------------------------------------