├── PeekPop.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── hungcv8.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── PeekPop ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── MessageCell.swift ├── MessageCell.xib ├── ReactionView.swift ├── ReactionView.xib └── ViewController.swift └── README.md /PeekPop.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FF834E2D29E64F420099ADF2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF834E2C29E64F420099ADF2 /* AppDelegate.swift */; }; 11 | FF834E3129E64F420099ADF2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF834E3029E64F420099ADF2 /* ViewController.swift */; }; 12 | FF834E3429E64F420099ADF2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FF834E3229E64F420099ADF2 /* Main.storyboard */; }; 13 | FF834E3629E64F430099ADF2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF834E3529E64F430099ADF2 /* Assets.xcassets */; }; 14 | FF834E3929E64F430099ADF2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FF834E3729E64F430099ADF2 /* LaunchScreen.storyboard */; }; 15 | FF83C57B29E805E700C06BC5 /* ReactionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = FF83C57A29E805E700C06BC5 /* ReactionView.xib */; }; 16 | FF83C57D29E805F800C06BC5 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF83C57C29E805F800C06BC5 /* ReactionView.swift */; }; 17 | FF83C58029E8142600C06BC5 /* MessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF83C57E29E8142600C06BC5 /* MessageCell.swift */; }; 18 | FF83C58129E8142600C06BC5 /* MessageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = FF83C57F29E8142600C06BC5 /* MessageCell.xib */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | FF834E2929E64F420099ADF2 /* PeekPop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PeekPop.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | FF834E2C29E64F420099ADF2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | FF834E3029E64F420099ADF2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | FF834E3329E64F420099ADF2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | FF834E3529E64F430099ADF2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | FF834E3829E64F430099ADF2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | FF834E3A29E64F430099ADF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | FF83C57A29E805E700C06BC5 /* ReactionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionView.xib; sourceTree = ""; }; 30 | FF83C57C29E805F800C06BC5 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = ""; }; 31 | FF83C57E29E8142600C06BC5 /* MessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCell.swift; sourceTree = ""; }; 32 | FF83C57F29E8142600C06BC5 /* MessageCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessageCell.xib; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | FF834E2629E64F420099ADF2 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | FF834E2029E64F420099ADF2 = { 47 | isa = PBXGroup; 48 | children = ( 49 | FF834E2B29E64F420099ADF2 /* PeekPop */, 50 | FF834E2A29E64F420099ADF2 /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | FF834E2A29E64F420099ADF2 /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | FF834E2929E64F420099ADF2 /* PeekPop.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | FF834E2B29E64F420099ADF2 /* PeekPop */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | FF83C57A29E805E700C06BC5 /* ReactionView.xib */, 66 | FF83C57C29E805F800C06BC5 /* ReactionView.swift */, 67 | FF834E2C29E64F420099ADF2 /* AppDelegate.swift */, 68 | FF834E3029E64F420099ADF2 /* ViewController.swift */, 69 | FF834E3229E64F420099ADF2 /* Main.storyboard */, 70 | FF834E3529E64F430099ADF2 /* Assets.xcassets */, 71 | FF834E3729E64F430099ADF2 /* LaunchScreen.storyboard */, 72 | FF834E3A29E64F430099ADF2 /* Info.plist */, 73 | FF83C57E29E8142600C06BC5 /* MessageCell.swift */, 74 | FF83C57F29E8142600C06BC5 /* MessageCell.xib */, 75 | ); 76 | path = PeekPop; 77 | sourceTree = ""; 78 | }; 79 | /* End PBXGroup section */ 80 | 81 | /* Begin PBXNativeTarget section */ 82 | FF834E2829E64F420099ADF2 /* PeekPop */ = { 83 | isa = PBXNativeTarget; 84 | buildConfigurationList = FF834E3D29E64F430099ADF2 /* Build configuration list for PBXNativeTarget "PeekPop" */; 85 | buildPhases = ( 86 | FF834E2529E64F420099ADF2 /* Sources */, 87 | FF834E2629E64F420099ADF2 /* Frameworks */, 88 | FF834E2729E64F420099ADF2 /* Resources */, 89 | ); 90 | buildRules = ( 91 | ); 92 | dependencies = ( 93 | ); 94 | name = PeekPop; 95 | productName = PeekPop; 96 | productReference = FF834E2929E64F420099ADF2 /* PeekPop.app */; 97 | productType = "com.apple.product-type.application"; 98 | }; 99 | /* End PBXNativeTarget section */ 100 | 101 | /* Begin PBXProject section */ 102 | FF834E2129E64F420099ADF2 /* Project object */ = { 103 | isa = PBXProject; 104 | attributes = { 105 | BuildIndependentTargetsInParallel = 1; 106 | LastSwiftUpdateCheck = 1420; 107 | LastUpgradeCheck = 1420; 108 | TargetAttributes = { 109 | FF834E2829E64F420099ADF2 = { 110 | CreatedOnToolsVersion = 14.2; 111 | }; 112 | }; 113 | }; 114 | buildConfigurationList = FF834E2429E64F420099ADF2 /* Build configuration list for PBXProject "PeekPop" */; 115 | compatibilityVersion = "Xcode 14.0"; 116 | developmentRegion = en; 117 | hasScannedForEncodings = 0; 118 | knownRegions = ( 119 | en, 120 | Base, 121 | ); 122 | mainGroup = FF834E2029E64F420099ADF2; 123 | productRefGroup = FF834E2A29E64F420099ADF2 /* Products */; 124 | projectDirPath = ""; 125 | projectRoot = ""; 126 | targets = ( 127 | FF834E2829E64F420099ADF2 /* PeekPop */, 128 | ); 129 | }; 130 | /* End PBXProject section */ 131 | 132 | /* Begin PBXResourcesBuildPhase section */ 133 | FF834E2729E64F420099ADF2 /* Resources */ = { 134 | isa = PBXResourcesBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | FF83C58129E8142600C06BC5 /* MessageCell.xib in Resources */, 138 | FF834E3929E64F430099ADF2 /* LaunchScreen.storyboard in Resources */, 139 | FF834E3629E64F430099ADF2 /* Assets.xcassets in Resources */, 140 | FF83C57B29E805E700C06BC5 /* ReactionView.xib in Resources */, 141 | FF834E3429E64F420099ADF2 /* Main.storyboard in Resources */, 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXResourcesBuildPhase section */ 146 | 147 | /* Begin PBXSourcesBuildPhase section */ 148 | FF834E2529E64F420099ADF2 /* Sources */ = { 149 | isa = PBXSourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | FF834E3129E64F420099ADF2 /* ViewController.swift in Sources */, 153 | FF83C58029E8142600C06BC5 /* MessageCell.swift in Sources */, 154 | FF83C57D29E805F800C06BC5 /* ReactionView.swift in Sources */, 155 | FF834E2D29E64F420099ADF2 /* AppDelegate.swift in Sources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXSourcesBuildPhase section */ 160 | 161 | /* Begin PBXVariantGroup section */ 162 | FF834E3229E64F420099ADF2 /* Main.storyboard */ = { 163 | isa = PBXVariantGroup; 164 | children = ( 165 | FF834E3329E64F420099ADF2 /* Base */, 166 | ); 167 | name = Main.storyboard; 168 | sourceTree = ""; 169 | }; 170 | FF834E3729E64F430099ADF2 /* LaunchScreen.storyboard */ = { 171 | isa = PBXVariantGroup; 172 | children = ( 173 | FF834E3829E64F430099ADF2 /* Base */, 174 | ); 175 | name = LaunchScreen.storyboard; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXVariantGroup section */ 179 | 180 | /* Begin XCBuildConfiguration section */ 181 | FF834E3B29E64F430099ADF2 /* Debug */ = { 182 | isa = XCBuildConfiguration; 183 | buildSettings = { 184 | ALWAYS_SEARCH_USER_PATHS = NO; 185 | CLANG_ANALYZER_NONNULL = YES; 186 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 188 | CLANG_ENABLE_MODULES = YES; 189 | CLANG_ENABLE_OBJC_ARC = YES; 190 | CLANG_ENABLE_OBJC_WEAK = YES; 191 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 192 | CLANG_WARN_BOOL_CONVERSION = YES; 193 | CLANG_WARN_COMMA = YES; 194 | CLANG_WARN_CONSTANT_CONVERSION = YES; 195 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 197 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 198 | CLANG_WARN_EMPTY_BODY = YES; 199 | CLANG_WARN_ENUM_CONVERSION = YES; 200 | CLANG_WARN_INFINITE_RECURSION = YES; 201 | CLANG_WARN_INT_CONVERSION = YES; 202 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 203 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 204 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 205 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 206 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 207 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 208 | CLANG_WARN_STRICT_PROTOTYPES = YES; 209 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 210 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | COPY_PHASE_STRIP = NO; 214 | DEBUG_INFORMATION_FORMAT = dwarf; 215 | ENABLE_STRICT_OBJC_MSGSEND = YES; 216 | ENABLE_TESTABILITY = YES; 217 | GCC_C_LANGUAGE_STANDARD = gnu11; 218 | GCC_DYNAMIC_NO_PIC = NO; 219 | GCC_NO_COMMON_BLOCKS = YES; 220 | GCC_OPTIMIZATION_LEVEL = 0; 221 | GCC_PREPROCESSOR_DEFINITIONS = ( 222 | "DEBUG=1", 223 | "$(inherited)", 224 | ); 225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 227 | GCC_WARN_UNDECLARED_SELECTOR = YES; 228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 229 | GCC_WARN_UNUSED_FUNCTION = YES; 230 | GCC_WARN_UNUSED_VARIABLE = YES; 231 | IPHONEOS_DEPLOYMENT_TARGET = 16.2; 232 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 233 | MTL_FAST_MATH = YES; 234 | ONLY_ACTIVE_ARCH = YES; 235 | SDKROOT = iphoneos; 236 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 237 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 238 | }; 239 | name = Debug; 240 | }; 241 | FF834E3C29E64F430099ADF2 /* Release */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | ALWAYS_SEARCH_USER_PATHS = NO; 245 | CLANG_ANALYZER_NONNULL = YES; 246 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 248 | CLANG_ENABLE_MODULES = YES; 249 | CLANG_ENABLE_OBJC_ARC = YES; 250 | CLANG_ENABLE_OBJC_WEAK = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 267 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 268 | CLANG_WARN_STRICT_PROTOTYPES = YES; 269 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 270 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 271 | CLANG_WARN_UNREACHABLE_CODE = YES; 272 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 275 | ENABLE_NS_ASSERTIONS = NO; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu11; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 280 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 281 | GCC_WARN_UNDECLARED_SELECTOR = YES; 282 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 283 | GCC_WARN_UNUSED_FUNCTION = YES; 284 | GCC_WARN_UNUSED_VARIABLE = YES; 285 | IPHONEOS_DEPLOYMENT_TARGET = 16.2; 286 | MTL_ENABLE_DEBUG_INFO = NO; 287 | MTL_FAST_MATH = YES; 288 | SDKROOT = iphoneos; 289 | SWIFT_COMPILATION_MODE = wholemodule; 290 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 291 | VALIDATE_PRODUCT = YES; 292 | }; 293 | name = Release; 294 | }; 295 | FF834E3E29E64F430099ADF2 /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 299 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 300 | CODE_SIGN_IDENTITY = "Apple Development"; 301 | CODE_SIGN_STYLE = Automatic; 302 | CURRENT_PROJECT_VERSION = 1; 303 | DEVELOPMENT_TEAM = DGUQZ567FN; 304 | GENERATE_INFOPLIST_FILE = YES; 305 | INFOPLIST_FILE = PeekPop/Info.plist; 306 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 307 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 308 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 309 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 310 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 311 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 312 | LD_RUNPATH_SEARCH_PATHS = ( 313 | "$(inherited)", 314 | "@executable_path/Frameworks", 315 | ); 316 | MARKETING_VERSION = 1.0; 317 | PRODUCT_BUNDLE_IDENTIFIER = com.hungcv8.PeekPop; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | PROVISIONING_PROFILE_SPECIFIER = ""; 320 | SWIFT_EMIT_LOC_STRINGS = YES; 321 | SWIFT_VERSION = 5.0; 322 | TARGETED_DEVICE_FAMILY = "1,2"; 323 | }; 324 | name = Debug; 325 | }; 326 | FF834E3F29E64F430099ADF2 /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 330 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 331 | CODE_SIGN_IDENTITY = "Apple Development"; 332 | CODE_SIGN_STYLE = Automatic; 333 | CURRENT_PROJECT_VERSION = 1; 334 | DEVELOPMENT_TEAM = DGUQZ567FN; 335 | GENERATE_INFOPLIST_FILE = YES; 336 | INFOPLIST_FILE = PeekPop/Info.plist; 337 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 338 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 339 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 340 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 341 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 343 | LD_RUNPATH_SEARCH_PATHS = ( 344 | "$(inherited)", 345 | "@executable_path/Frameworks", 346 | ); 347 | MARKETING_VERSION = 1.0; 348 | PRODUCT_BUNDLE_IDENTIFIER = com.hungcv8.PeekPop; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | PROVISIONING_PROFILE_SPECIFIER = ""; 351 | SWIFT_EMIT_LOC_STRINGS = YES; 352 | SWIFT_VERSION = 5.0; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Release; 356 | }; 357 | /* End XCBuildConfiguration section */ 358 | 359 | /* Begin XCConfigurationList section */ 360 | FF834E2429E64F420099ADF2 /* Build configuration list for PBXProject "PeekPop" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | FF834E3B29E64F430099ADF2 /* Debug */, 364 | FF834E3C29E64F430099ADF2 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | FF834E3D29E64F430099ADF2 /* Build configuration list for PBXNativeTarget "PeekPop" */ = { 370 | isa = XCConfigurationList; 371 | buildConfigurations = ( 372 | FF834E3E29E64F430099ADF2 /* Debug */, 373 | FF834E3F29E64F430099ADF2 /* Release */, 374 | ); 375 | defaultConfigurationIsVisible = 0; 376 | defaultConfigurationName = Release; 377 | }; 378 | /* End XCConfigurationList section */ 379 | }; 380 | rootObject = FF834E2129E64F420099ADF2 /* Project object */; 381 | } 382 | -------------------------------------------------------------------------------- /PeekPop.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PeekPop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PeekPop.xcodeproj/xcuserdata/hungcv8.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /PeekPop.xcodeproj/xcuserdata/hungcv8.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PeekPop.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PeekPop/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // PeekPop 4 | // 5 | // Created by Chu Hung on 12/04/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /PeekPop/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PeekPop/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PeekPop/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PeekPop/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /PeekPop/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /PeekPop/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PeekPop/MessageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageCell.swift 3 | // PeekPop 4 | // 5 | // Created by Chu Hung on 13/04/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | class MessageCell: UITableViewCell { 11 | 12 | @IBOutlet weak var wrapperView: UIView! 13 | @IBOutlet weak var messageLB: UILabel! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | selectionStyle = .none 18 | wrapperView.layer.masksToBounds = true 19 | wrapperView.layer.cornerRadius = 10 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PeekPop/MessageCell.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 | 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 | -------------------------------------------------------------------------------- /PeekPop/ReactionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReactionView.swift 3 | // PeekPop 4 | // 5 | // Created by Chu Hung on 13/04/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | enum ReactionType { 11 | case like 12 | case love 13 | case angry 14 | case sad 15 | } 16 | 17 | class ReactionView: UIView { 18 | 19 | @IBOutlet weak var stackView: UIStackView! 20 | 21 | var onReaction: ((ReactionType) -> Void)? 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | initViewWithNib() 26 | doAnimation() 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | super.init(coder: coder) 31 | initViewWithNib() 32 | } 33 | 34 | func doAnimation() { 35 | let offset = CGPoint(x: UIScreen.main.bounds.width, y: 0) 36 | self.transform = CGAffineTransform(translationX: offset.x, y: offset.y) 37 | UIView.animate(withDuration: 1, 38 | delay: 0, 39 | usingSpringWithDamping: 1, 40 | initialSpringVelocity: 1, 41 | options: .curveEaseOut, 42 | animations: { 43 | self.transform = .identity 44 | self.alpha = 1 45 | }, 46 | completion: { [weak self] _ in 47 | guard let self = self else { return } 48 | self.stackView.arrangedSubviews.forEach { view in 49 | UIView.animate(withDuration: 0.5, 50 | delay: 0.0, 51 | options: .curveEaseIn, 52 | animations: { 53 | view.transform = CGAffineTransform.identity.scaledBy(x: 1.5, y: 1.5) 54 | }, completion: { _ in 55 | UIView.animate(withDuration: 0.5, animations: { 56 | view.transform = .identity 57 | }) 58 | }) 59 | } 60 | }) 61 | } 62 | 63 | @IBAction func doLike(_ sender: Any) { 64 | onReaction?(.like) 65 | } 66 | @IBAction func doLove(_ sender: Any) { 67 | onReaction?(.love) 68 | } 69 | @IBAction func doAngry(_ sender: Any) { 70 | onReaction?(.angry) 71 | } 72 | @IBAction func doSad(_ sender: Any) { 73 | onReaction?(.sad) 74 | } 75 | 76 | } 77 | 78 | extension UIView { 79 | 80 | func loadViewFromNib() -> UIView? { 81 | let bundle = Bundle.main 82 | let nib = UINib(nibName: "\(type(of: self))", bundle: bundle) 83 | return nib.instantiate( 84 | withOwner: self, 85 | options: nil).first as? UIView 86 | } 87 | 88 | func initViewWithNib() { 89 | guard let view = loadViewFromNib() else { return } 90 | view.frame = bounds 91 | view.autoresizingMask = 92 | [.flexibleWidth, .flexibleHeight] 93 | addSubview(view) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /PeekPop/ReactionView.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 | 32 | 40 | 48 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /PeekPop/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // PeekPop 4 | // 5 | // Created by Chu Hung on 12/04/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | @IBOutlet weak var tableView: UITableView! 13 | 14 | var messages = [String]() 15 | let count = 20 16 | let reactionHeight: CGFloat = 40.0 17 | let spaceReactionHeight: CGFloat = 10.0 18 | let menuHeight: CGFloat = 200 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell") 24 | tableView.delegate = self 25 | tableView.dataSource = self 26 | tableView.allowsSelection = false 27 | initData() 28 | } 29 | 30 | func initData() { 31 | for _ in 0...init(uncheckedBounds: (lower: 10, upper: 1000))))) 33 | } 34 | tableView.reloadData() 35 | } 36 | func randomString(length: Int) -> String { 37 | let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " 38 | return String((0.. UIMenu { 43 | let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) {_ in } 44 | let rename = UIAction(title: "Mark as read", image: UIImage(systemName: "square.and.pencil")) { _ in } 45 | let delete = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) {_ in } 46 | return UIMenu(title: "", children: [share, rename, delete]) 47 | } 48 | } 49 | 50 | extension ViewController: UITableViewDelegate, UITableViewDataSource { 51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 52 | return messages.count 53 | } 54 | 55 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 56 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell") as? MessageCell else { 57 | return UITableViewCell() 58 | } 59 | cell.messageLB.text = messages[indexPath.row] 60 | return cell 61 | } 62 | 63 | @available(iOS 13.0, *) 64 | func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { 65 | let identifier = NSString(string: "\(indexPath.row)") 66 | return UIContextMenuConfiguration(identifier: identifier, previewProvider: nil) { [weak self] _ in 67 | guard let self = self else { return UIMenu() } 68 | return self.createContextMenu() 69 | } 70 | } 71 | 72 | @available(iOS 13.0, *) 73 | func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? { 74 | makeTargetedPreview(for: configuration) 75 | } 76 | 77 | @available(iOS 13.0, *) 78 | func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? { 79 | makeTargetedDismissPreview(for: configuration) 80 | } 81 | 82 | @available(iOS 13.0, *) 83 | func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { 84 | animator.preferredCommitStyle = .pop 85 | } 86 | 87 | @available(iOS 13.0, *) 88 | func makeTargetedPreview(for configuration: UIContextMenuConfiguration) -> UITargetedPreview? { 89 | guard let identifier = configuration.identifier as? String else { return nil } 90 | guard let row = Int(identifier) else { return nil } 91 | guard let cell = tableView.cellForRow(at: .init(row: row, section: 0)) as? MessageCell else { return nil } 92 | guard let snapshot = cell.resizableSnapshotView(from: CGRect(origin: .zero, 93 | size: CGSize(width: cell.bounds.width, height: min(cell.bounds.height, UIScreen.main.bounds.height - reactionHeight - spaceReactionHeight - menuHeight))), 94 | afterScreenUpdates: false, 95 | withCapInsets: UIEdgeInsets.zero) else { return nil } 96 | 97 | let reactionView = ReactionView() 98 | reactionView.onReaction = { [weak self] reactionType in 99 | guard let self = self else { return } 100 | print(reactionType) 101 | self.dismiss(animated: true) 102 | } 103 | reactionView.layer.cornerRadius = 10 104 | reactionView.layer.masksToBounds = true 105 | reactionView.translatesAutoresizingMaskIntoConstraints = false 106 | 107 | snapshot.layer.cornerRadius = 10 108 | snapshot.layer.masksToBounds = true 109 | snapshot.translatesAutoresizingMaskIntoConstraints = false 110 | 111 | let container = UIView(frame: CGRect(origin: .zero, 112 | size: CGSize(width: cell.bounds.width, 113 | height: snapshot.bounds.height + reactionHeight + spaceReactionHeight))) 114 | container.backgroundColor = .clear 115 | container.addSubview(reactionView) 116 | container.addSubview(snapshot) 117 | 118 | snapshot.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true 119 | snapshot.topAnchor.constraint(equalTo: container.topAnchor).isActive = true 120 | snapshot.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true 121 | snapshot.bottomAnchor.constraint(equalTo: reactionView.topAnchor, constant: -spaceReactionHeight).isActive = true 122 | 123 | reactionView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10).isActive = true 124 | reactionView.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true 125 | reactionView.widthAnchor.constraint(equalToConstant: 50*4).isActive = true 126 | reactionView.heightAnchor.constraint(equalToConstant: reactionHeight).isActive = true 127 | 128 | let centerPoint = CGPoint(x: cell.center.x, y: cell.center.y) 129 | let previewTarget = UIPreviewTarget(container: tableView, center: centerPoint) 130 | let parameters = UIPreviewParameters() 131 | parameters.backgroundColor = .clear 132 | if #available(iOS 14.0, *) { 133 | parameters.shadowPath = UIBezierPath() 134 | } 135 | return UITargetedPreview(view: container, parameters: parameters, target: previewTarget) 136 | } 137 | 138 | @available(iOS 13.0, *) 139 | func makeTargetedDismissPreview(for configuration: UIContextMenuConfiguration) -> UITargetedPreview? { 140 | guard let identifier = configuration.identifier as? String else { return nil } 141 | guard let row = Int(identifier) else { return nil } 142 | guard let cell = tableView.cellForRow(at: .init(row: row, section: 0)) as? MessageCell else { return nil } 143 | guard let snapshot = cell.resizableSnapshotView(from: CGRect(origin: .zero, 144 | size: CGSize(width: cell.bounds.width, height: min(cell.bounds.height, UIScreen.main.bounds.height - reactionHeight - spaceReactionHeight - menuHeight))), 145 | afterScreenUpdates: false, 146 | withCapInsets: UIEdgeInsets.zero) else { return nil } 147 | 148 | let centerPoint = CGPoint(x: cell.center.x, y: cell.center.y) 149 | let previewTarget = UIPreviewTarget(container: tableView, center: centerPoint) 150 | let parameters = UIPreviewParameters() 151 | parameters.backgroundColor = .clear 152 | if #available(iOS 14.0, *) { 153 | parameters.shadowPath = UIBezierPath() 154 | } 155 | return UITargetedPreview(view: snapshot, parameters: parameters, target: previewTarget) 156 | } 157 | 158 | } 159 | 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reaction-message-ContextMenu 2 | Reaction message using ContextMenu 3 | 4 | ![simulator_screenshot_A0F2ED67-FE2C-4FEB-8256-2EBD7A17107F](https://user-images.githubusercontent.com/20833690/233256561-bf98c876-585b-4f2c-88b5-f2ef3b9bafe5.png) 5 | ![simulator_screenshot_AC43AEEA-247C-4FC7-953D-F6BBA3DC2E4C](https://user-images.githubusercontent.com/20833690/233256586-b0f53e3f-9dd6-4d41-b1fa-ca18bb15e6d3.png) 6 | --------------------------------------------------------------------------------