diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0353cf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint +*.DS_Store + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output \ No newline at end of file diff --git a/DragAndDropDemo.xcodeproj/project.pbxproj b/DragAndDropDemo.xcodeproj/project.pbxproj new file mode 100755 index 0000000..ab53de4 --- /dev/null +++ b/DragAndDropDemo.xcodeproj/project.pbxproj @@ -0,0 +1,456 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + AC7F2B701EE19F0300F98D2B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0EEB1ED895E900498794 /* Main.storyboard */; }; + AC7F2B7D1EE1A63F00F98D2B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0EEA1ED895E900498794 /* LaunchScreen.storyboard */; }; + AC8353A51EDAC49100DED9D3 /* DropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8353A41EDAC49100DED9D3 /* DropViewController.swift */; }; + ACCA5DF91EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF61EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift */; }; + ACCA5DFA1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF61EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift */; }; + ACCA5DFB1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF71EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift */; }; + ACCA5DFC1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF71EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift */; }; + ACCA5DFD1EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF81EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift */; }; + ACCA5DFE1EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACCA5DF81EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift */; }; + ACE2973A1ED97CB100595DBA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE297391ED97CB100595DBA /* AppDelegate.swift */; }; + ACE2973B1ED97CB100595DBA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE297391ED97CB100595DBA /* AppDelegate.swift */; }; + ACFF0ED01ED895B000498794 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0ECF1ED895B000498794 /* Assets.xcassets */; }; + ACFF0EE11ED895BA00498794 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0ECF1ED895B000498794 /* Assets.xcassets */; }; + ACFF0EEE1ED895E900498794 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0EEA1ED895E900498794 /* LaunchScreen.storyboard */; }; + ACFF0EEF1ED895E900498794 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACFF0EEB1ED895E900498794 /* Main.storyboard */; }; + ACFF0EF11ED8962000498794 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFF0EF01ED8962000498794 /* AppConfig.swift */; }; + ACFF0EF21ED8964500498794 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFF0EF01ED8962000498794 /* AppConfig.swift */; }; + ACFF0EF41ED896F000498794 /* DragViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFF0EF31ED896F000498794 /* DragViewController.swift */; }; + ACFF0EFC1ED89C3900498794 /* DragViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFF0EF31ED896F000498794 /* DragViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + AC7F2B751EE1A53100F98D2B /* Drag-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Drag-Info.plist"; sourceTree = ""; }; + AC7F2B761EE1A53100F98D2B /* Drag.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Drag.entitlements; sourceTree = ""; }; + AC7F2B771EE1A53100F98D2B /* Drop-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Drop-Info.plist"; sourceTree = ""; }; + AC7F2B781EE1A53100F98D2B /* Drop.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Drop.entitlements; sourceTree = ""; }; + AC8353A41EDAC49100DED9D3 /* DropViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropViewController.swift; sourceTree = ""; }; + ACCA5DF61EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SplitViewDragAndDrop+Dragger.swift"; sourceTree = ""; }; + ACCA5DF71EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SplitViewDragAndDrop+Utils.swift"; sourceTree = ""; }; + ACCA5DF81EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitViewDragAndDrop.swift; sourceTree = ""; }; + ACE297391ED97CB100595DBA /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + ACFF0EC51ED895B000498794 /* Drag.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Drag.app; sourceTree = BUILT_PRODUCTS_DIR; }; + ACFF0ECF1ED895B000498794 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + ACFF0EE61ED895BA00498794 /* Drop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Drop.app; sourceTree = BUILT_PRODUCTS_DIR; }; + ACFF0EEA1ED895E900498794 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + ACFF0EEB1ED895E900498794 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + ACFF0EF01ED8962000498794 /* AppConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; + ACFF0EF31ED896F000498794 /* DragViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + ACFF0EC21ED895B000498794 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ACFF0EDE1ED895BA00498794 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + AC7F2B741EE1A53100F98D2B /* Supporting files */ = { + isa = PBXGroup; + children = ( + AC7F2B761EE1A53100F98D2B /* Drag.entitlements */, + AC7F2B781EE1A53100F98D2B /* Drop.entitlements */, + AC7F2B751EE1A53100F98D2B /* Drag-Info.plist */, + AC7F2B771EE1A53100F98D2B /* Drop-Info.plist */, + ); + path = "Supporting files"; + sourceTree = ""; + }; + ACCA5DF51EE1A96E00A0FDC3 /* SplitViewDragAndDrop */ = { + isa = PBXGroup; + children = ( + ACCA5DF61EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift */, + ACCA5DF71EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift */, + ACCA5DF81EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift */, + ); + path = SplitViewDragAndDrop; + sourceTree = ""; + }; + ACFF0EBC1ED895B000498794 = { + isa = PBXGroup; + children = ( + ACFF0EC71ED895B000498794 /* DragAndDropDemo */, + ACFF0EC61ED895B000498794 /* Products */, + ACCA5DF51EE1A96E00A0FDC3 /* SplitViewDragAndDrop */, + ); + sourceTree = ""; + }; + ACFF0EC61ED895B000498794 /* Products */ = { + isa = PBXGroup; + children = ( + ACFF0EC51ED895B000498794 /* Drag.app */, + ACFF0EE61ED895BA00498794 /* Drop.app */, + ); + name = Products; + sourceTree = ""; + }; + ACFF0EC71ED895B000498794 /* DragAndDropDemo */ = { + isa = PBXGroup; + children = ( + ACFF0EEA1ED895E900498794 /* LaunchScreen.storyboard */, + ACFF0EEB1ED895E900498794 /* Main.storyboard */, + ACFF0EF01ED8962000498794 /* AppConfig.swift */, + ACE297391ED97CB100595DBA /* AppDelegate.swift */, + ACFF0EF31ED896F000498794 /* DragViewController.swift */, + AC8353A41EDAC49100DED9D3 /* DropViewController.swift */, + ACFF0ECF1ED895B000498794 /* Assets.xcassets */, + AC7F2B741EE1A53100F98D2B /* Supporting files */, + ); + path = DragAndDropDemo; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + ACFF0EC41ED895B000498794 /* Drag */ = { + isa = PBXNativeTarget; + buildConfigurationList = ACFF0ED71ED895B000498794 /* Build configuration list for PBXNativeTarget "Drag" */; + buildPhases = ( + ACFF0EC11ED895B000498794 /* Sources */, + ACFF0EC21ED895B000498794 /* Frameworks */, + ACFF0EC31ED895B000498794 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Drag; + productName = DragAndDropDemo; + productReference = ACFF0EC51ED895B000498794 /* Drag.app */; + productType = "com.apple.product-type.application"; + }; + ACFF0EDA1ED895BA00498794 /* Drop */ = { + isa = PBXNativeTarget; + buildConfigurationList = ACFF0EE31ED895BA00498794 /* Build configuration list for PBXNativeTarget "Drop" */; + buildPhases = ( + ACFF0EDB1ED895BA00498794 /* Sources */, + ACFF0EDE1ED895BA00498794 /* Frameworks */, + ACFF0EDF1ED895BA00498794 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Drop; + productName = DragAndDropDemo; + productReference = ACFF0EE61ED895BA00498794 /* Drop.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + ACFF0EBD1ED895B000498794 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = Mario; + TargetAttributes = { + ACFF0EC41ED895B000498794 = { + CreatedOnToolsVersion = 8.3.2; + DevelopmentTeam = X9253HG4XT; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; + ACFF0EDA1ED895BA00498794 = { + DevelopmentTeam = X9253HG4XT; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = ACFF0EC01ED895B000498794 /* Build configuration list for PBXProject "DragAndDropDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = ACFF0EBC1ED895B000498794; + productRefGroup = ACFF0EC61ED895B000498794 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ACFF0EC41ED895B000498794 /* Drag */, + ACFF0EDA1ED895BA00498794 /* Drop */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + ACFF0EC31ED895B000498794 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ACFF0EEF1ED895E900498794 /* Main.storyboard in Resources */, + ACFF0EEE1ED895E900498794 /* LaunchScreen.storyboard in Resources */, + ACFF0ED01ED895B000498794 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ACFF0EDF1ED895BA00498794 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AC7F2B7D1EE1A63F00F98D2B /* LaunchScreen.storyboard in Resources */, + ACFF0EE11ED895BA00498794 /* Assets.xcassets in Resources */, + AC7F2B701EE19F0300F98D2B /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + ACFF0EC11ED895B000498794 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ACCA5DFD1EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift in Sources */, + ACCA5DF91EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift in Sources */, + ACFF0EF41ED896F000498794 /* DragViewController.swift in Sources */, + ACE2973A1ED97CB100595DBA /* AppDelegate.swift in Sources */, + ACFF0EF11ED8962000498794 /* AppConfig.swift in Sources */, + ACCA5DFB1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ACFF0EDB1ED895BA00498794 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ACCA5DFE1EE1A96E00A0FDC3 /* SplitViewDragAndDrop.swift in Sources */, + ACFF0EF21ED8964500498794 /* AppConfig.swift in Sources */, + ACFF0EFC1ED89C3900498794 /* DragViewController.swift in Sources */, + ACCA5DFC1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Utils.swift in Sources */, + ACE2973B1ED97CB100595DBA /* AppDelegate.swift in Sources */, + ACCA5DFA1EE1A96E00A0FDC3 /* SplitViewDragAndDrop+Dragger.swift in Sources */, + AC8353A51EDAC49100DED9D3 /* DropViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + ACFF0ED51ED895B000498794 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + ACFF0ED61ED895B000498794 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + ACFF0ED81ED895B000498794 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "DragAndDropDemo/Supporting files/Drag.entitlements"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = X9253HG4XT; + INFOPLIST_FILE = "$(SRCROOT)/DragAndDropDemo/Supporting files/Drag-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_SWIFT_FLAGS = "-D IS_DRAG_APP"; + PRODUCT_BUNDLE_IDENTIFIER = com.marioiannotta.DragAndDropDemo.DragApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = 2; + }; + name = Debug; + }; + ACFF0ED91ED895B000498794 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "DragAndDropDemo/Supporting files/Drag.entitlements"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = X9253HG4XT; + INFOPLIST_FILE = "$(SRCROOT)/DragAndDropDemo/Supporting files/Drag-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_SWIFT_FLAGS = "-D IS_DRAG_APP"; + PRODUCT_BUNDLE_IDENTIFIER = com.marioiannotta.DragAndDropDemo.DragApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = 2; + }; + name = Release; + }; + ACFF0EE41ED895BA00498794 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "DragAndDropDemo/Supporting files/Drop.entitlements"; + DEVELOPMENT_TEAM = X9253HG4XT; + INFOPLIST_FILE = "$(SRCROOT)/DragAndDropDemo/Supporting files/Drop-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.marioiannotta.DragAndDropDemo.DropApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = 2; + }; + name = Debug; + }; + ACFF0EE51ED895BA00498794 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "DragAndDropDemo/Supporting files/Drop.entitlements"; + DEVELOPMENT_TEAM = X9253HG4XT; + INFOPLIST_FILE = "$(SRCROOT)/DragAndDropDemo/Supporting files/Drop-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.marioiannotta.DragAndDropDemo.DropApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = 2; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + ACFF0EC01ED895B000498794 /* Build configuration list for PBXProject "DragAndDropDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ACFF0ED51ED895B000498794 /* Debug */, + ACFF0ED61ED895B000498794 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ACFF0ED71ED895B000498794 /* Build configuration list for PBXNativeTarget "Drag" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ACFF0ED81ED895B000498794 /* Debug */, + ACFF0ED91ED895B000498794 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ACFF0EE31ED895BA00498794 /* Build configuration list for PBXNativeTarget "Drop" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ACFF0EE41ED895BA00498794 /* Debug */, + ACFF0EE51ED895BA00498794 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = ACFF0EBD1ED895B000498794 /* Project object */; +} diff --git a/DragAndDropDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DragAndDropDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/DragAndDropDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DragAndDropDemo/AppConfig.swift b/DragAndDropDemo/AppConfig.swift new file mode 100755 index 0000000..6658907 --- /dev/null +++ b/DragAndDropDemo/AppConfig.swift @@ -0,0 +1,23 @@ +// +// AppConfig.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import Foundation + +struct AppConfig { + + static var isDragApp: Bool { + + #if IS_DRAG_APP + return true + #endif + + return false + + } + +} diff --git a/DragAndDropDemo/AppDelegate.swift b/DragAndDropDemo/AppDelegate.swift new file mode 100755 index 0000000..6cee9ee --- /dev/null +++ b/DragAndDropDemo/AppDelegate.swift @@ -0,0 +1,35 @@ +// +// AppDelegate.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + + SplitViewDragAndDrop.configure(groupIdentifier: "group.com.marioiannotta.draganddropdemo") + + window = UIWindow(frame: UIScreen.main.bounds) + + if AppConfig.isDragApp { + window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DragViewController") + } else { + window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DropViewController") + } + + window?.makeKeyAndVisible() + + return true + + } + +} + diff --git a/DragAndDropDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/DragAndDropDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 0000000..36d2c80 --- /dev/null +++ b/DragAndDropDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DragAndDropDemo/DragViewController.swift b/DragAndDropDemo/DragViewController.swift new file mode 100755 index 0000000..22d8b54 --- /dev/null +++ b/DragAndDropDemo/DragViewController.swift @@ -0,0 +1,26 @@ +// +// DragViewController.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +class DragViewController: UIViewController { + + @IBOutlet private var ciaoDraggableView: UIView! + @IBOutlet private var helloDraggableView: UIView! + @IBOutlet private var alohaDraggableView: UIView! + + override func viewDidLoad() { + super.viewDidLoad() + + SplitViewDragAndDrop.handleDrag(viewToDrag: ciaoDraggableView, identifier: "ciao_d&d", dataToTransfer: "Ciao!".data(using: .utf8)) + SplitViewDragAndDrop.handleDrag(viewToDrag: helloDraggableView, identifier: "hello_d&d", dataToTransfer: "Hello!".data(using: .utf8)) + SplitViewDragAndDrop.handleDrag(viewToDrag: alohaDraggableView, identifier: "aloha_d&d", dataToTransfer: "Aloha!".data(using: .utf8)) + + } + +} diff --git a/DragAndDropDemo/DropViewController.swift b/DragAndDropDemo/DropViewController.swift new file mode 100755 index 0000000..36f1cce --- /dev/null +++ b/DragAndDropDemo/DropViewController.swift @@ -0,0 +1,122 @@ +// +// DropViewController.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +class DropViewController: UIViewController { + + @IBOutlet private weak var ciaoTargetImageView: UIImageView! + @IBOutlet private weak var helloTargetImageView: UIImageView! + @IBOutlet private weak var alohaTargetImageView: UIImageView! + + private func presentAlertController(withMessage message: String) { + + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) + + present(alertController, animated: true, completion: nil) + + } + + private func draggingBeganAnimation(for view: UIView) { + + UIView.animate(withDuration: 0.3) { + + view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) + + } + + } + + private func draggingEndedAnimation(for view: UIView) { + + UIView.animate(withDuration: 0.3) { + + view.transform = CGAffineTransform.identity + + } + + } + + override func viewDidLoad() { + super.viewDidLoad() + + SplitViewDragAndDrop.addDropObserver( + targetView: ciaoTargetImageView, + identifier: "ciao_d&d", + draggingBegan: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingBeganAnimation(for: self.ciaoTargetImageView) + + }, + draggingValidation: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingEndedAnimation(for: self.ciaoTargetImageView) + + return self.ciaoTargetImageView.windowRelativeFrame.contains(frame) + + }, + completion: { frame, draggedViewSnapshotImage, dataTransfered, isValid in + + if isValid { + self.ciaoTargetImageView.image = draggedViewSnapshotImage + } + + } + ) + + SplitViewDragAndDrop.addDropObserver( + targetView: helloTargetImageView, + identifier: "hello_d&d", + draggingBegan: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingBeganAnimation(for: self.helloTargetImageView) + + }, + draggingValidation: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingEndedAnimation(for: self.helloTargetImageView) + return self.helloTargetImageView.windowRelativeFrame.contains(frame) + + }, + completion: { frame, draggedViewSnapshotImage, dataTransfered, isValid in + + if isValid { + self.helloTargetImageView.image = draggedViewSnapshotImage + } + + } + ) + + SplitViewDragAndDrop.addDropObserver( + targetView: alohaTargetImageView, + identifier: "aloha_d&d", + draggingBegan: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingBeganAnimation(for: self.alohaTargetImageView) + + }, + draggingValidation: { frame, draggedViewSnapshotImage, dataTransfered in + + self.draggingEndedAnimation(for: self.alohaTargetImageView) + return self.alohaTargetImageView.windowRelativeFrame.contains(frame) + + }, + completion: { frame, draggedViewSnapshotImage, dataTransfered, isValid in + + if isValid { + self.alohaTargetImageView.image = draggedViewSnapshotImage + } + + } + ) + + } + +} diff --git a/DragAndDropDemo/LaunchScreen.storyboard b/DragAndDropDemo/LaunchScreen.storyboard new file mode 100755 index 0000000..fdf3f97 --- /dev/null +++ b/DragAndDropDemo/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DragAndDropDemo/Main.storyboard b/DragAndDropDemo/Main.storyboard new file mode 100755 index 0000000..dd136b6 --- /dev/null +++ b/DragAndDropDemo/Main.storyboard @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DragAndDropDemo/Supporting files/Drag-Info.plist b/DragAndDropDemo/Supporting files/Drag-Info.plist new file mode 100755 index 0000000..d052473 --- /dev/null +++ b/DragAndDropDemo/Supporting files/Drag-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/DragAndDropDemo/Supporting files/Drag.entitlements b/DragAndDropDemo/Supporting files/Drag.entitlements new file mode 100755 index 0000000..3f7ef17 --- /dev/null +++ b/DragAndDropDemo/Supporting files/Drag.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.marioiannotta.draganddropdemo + + + diff --git a/DragAndDropDemo/Supporting files/Drop-Info.plist b/DragAndDropDemo/Supporting files/Drop-Info.plist new file mode 100755 index 0000000..c62ed13 --- /dev/null +++ b/DragAndDropDemo/Supporting files/Drop-Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/DragAndDropDemo/Supporting files/Drop.entitlements b/DragAndDropDemo/Supporting files/Drop.entitlements new file mode 100755 index 0000000..3f7ef17 --- /dev/null +++ b/DragAndDropDemo/Supporting files/Drop.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.marioiannotta.draganddropdemo + + + diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..631b057 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Mario Iannotta + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..d5b499c --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# DragAndDropCrossApp +Easily add drag and drop to pass data between your apps diff --git a/SplitViewDragAndDrop/.DS_Store b/SplitViewDragAndDrop/.DS_Store new file mode 100755 index 0000000..5008ddf Binary files /dev/null and b/SplitViewDragAndDrop/.DS_Store differ diff --git a/SplitViewDragAndDrop/SplitViewDragAndDrop+Dragger.swift b/SplitViewDragAndDrop/SplitViewDragAndDrop+Dragger.swift new file mode 100755 index 0000000..aaa7905 --- /dev/null +++ b/SplitViewDragAndDrop/SplitViewDragAndDrop+Dragger.swift @@ -0,0 +1,194 @@ +// +// SplitViewDragAndDrop+Dragger.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +extension SplitViewDragAndDrop { + + internal class Dragger: NSObject { + + private struct StoreKeys { + + static let draggingValidation = "storeKeysDraggingValidation" + + struct DraggingValidation { + + static let result = "storeKeysDraggingValidationResult" + static let initialDragPoint = "storeKeysDraggingValidationInitialDragPoint" + + } + + } + + private var dragAndDropManager: SplitViewDragAndDrop + + private var initialDragPoint: CGPoint! + private var viewToDragSnapshotImageView = UIImageView(frame: CGRect.zero) + private var updateTriggered = false + private var updateTriggeredToRight = false + + private var dataToTransfers = [UIView: Data]() + private var identifiers = [UIView: String]() + + internal var draggingEndedClosure: ((_ isValid: Bool) -> Void)? + + internal init(dragAndDropManager: SplitViewDragAndDrop) { + self.dragAndDropManager = dragAndDropManager + + super.init() + dragAndDropManager.groupDefaults.addObserver(self, forKeyPath: StoreKeys.draggingValidation, options: .new, context: nil) + } + + func notifyDraggingValidationResult(_ result: Bool, initialDragPoint: CGPoint) { + + let draggingValidationDictionary: [String: Any] = [ + StoreKeys.DraggingValidation.result: result, + StoreKeys.DraggingValidation.initialDragPoint: initialDragPoint + ] + dragAndDropManager.groupDefaults.set(NSKeyedArchiver.archivedData(withRootObject: draggingValidationDictionary), forKey: StoreKeys.draggingValidation) + dragAndDropManager.groupDefaults.synchronize() + + } + + deinit { + + dragAndDropManager.groupDefaults.removeObserver(self, forKeyPath: StoreKeys.draggingValidation) + + } + + private func setupViewToDragSnapshotImageView(from view: UIView, centerPoint: CGPoint) { + + let image = view.getSnapshot() + + viewToDragSnapshotImageView.frame = view.frame + viewToDragSnapshotImageView.center = centerPoint + viewToDragSnapshotImageView.image = image + + UIApplication.shared.keyWindow?.addSubview(viewToDragSnapshotImageView) + + } + + internal func handleDrag(viewToDrag: UIView, identifier: String, dataToTransfer: Data? = nil) { + + viewToDrag.addGestureRecognizer( + UIPanGestureRecognizer(target: self, action: #selector(handleGestureRecognizer(_:))) + ) + + self.dataToTransfers[viewToDrag] = dataToTransfer + self.identifiers[viewToDrag] = identifier + + } + + @objc private func handleGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) { + + guard + let draggedView = gestureRecognizer.view, + let keyWindow = UIApplication.shared.keyWindow + else { return } + + let dragPoint = gestureRecognizer.location(in: keyWindow) + + switch gestureRecognizer.state { + + case .began: + + initialDragPoint = dragPoint + updateTriggered = false + updateTriggeredToRight = false + + setupViewToDragSnapshotImageView(from: draggedView, centerPoint: dragPoint) + + if let viewToDragSnapshotImage = viewToDragSnapshotImageView.image { + + dragAndDropManager.prepareForDraggingUpdate( + identifier: identifiers[draggedView] ?? "", + viewToDragSnapshotImage: viewToDragSnapshotImage, + dataToTransfer: dataToTransfers[draggedView] + ) + + } + + dragAndDropManager.notifyGestureRecognizerUpdate(state: .began, isTriggeredToRight: updateTriggeredToRight) + + case .ended: + + if !updateTriggered { + SplitViewDragAndDrop.completeDragging(isFallBack: true, draggingView: self.viewToDragSnapshotImageView, targetCenterPoint: self.initialDragPoint, completion: nil) + } + + dragAndDropManager.notifyGestureRecognizerUpdate(state: .ended, isTriggeredToRight: updateTriggeredToRight) + + default: + + let isToRight = gestureRecognizer.velocity(in: keyWindow).x > 0 + viewToDragSnapshotImageView.center = dragPoint + + let shouldTriggerUpdate = isToRight ? + viewToDragSnapshotImageView.frame.origin.x + viewToDragSnapshotImageView.frame.size.width >= UIWindow.width : + viewToDragSnapshotImageView.frame.origin.x <= 0 + + if updateTriggered || shouldTriggerUpdate { + + if updateTriggered == false { + updateTriggeredToRight = isToRight + } + + updateTriggered = true + + let frame = CGRect( + x: SplitViewDragAndDrop.transformXCoordinate(viewToDragSnapshotImageView.frame.origin.x, updateTriggeredToRight: updateTriggeredToRight), + y: viewToDragSnapshotImageView.frame.origin.y, + width: viewToDragSnapshotImageView.frame.size.width, + height: viewToDragSnapshotImageView.frame.size.height + ) + + var initialDragPoint = self.initialDragPoint ?? .zero + initialDragPoint.x = SplitViewDragAndDrop.transformXCoordinate(initialDragPoint.x, updateTriggeredToRight: updateTriggeredToRight) + + dragAndDropManager.notifyDraggingUpdate(frame: frame, initialDragPoint: initialDragPoint) + + } + + dragAndDropManager.notifyGestureRecognizerUpdate(state: .changed, isTriggeredToRight: updateTriggeredToRight) + + } + + } + + internal override func observeValue( + forKeyPath keyPath: String?, of object: Any?, + change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + + guard let keyPath = keyPath, let changedData = change?[.newKey] as? Data else { return } + + switch keyPath { + + case StoreKeys.draggingValidation: + + if + let draggingValidation = NSKeyedUnarchiver.unarchiveObject(with: changedData) as? [String: Any], + let validationResult = draggingValidation[StoreKeys.DraggingValidation.result] as? Bool, validationResult == false, + let initialDragPoint = draggingValidation[StoreKeys.DraggingValidation.initialDragPoint] as? CGPoint, initialDragPoint.x > 0, initialDragPoint.x < UIWindow.width + { + + SplitViewDragAndDrop.completeDragging(isFallBack: true, draggingView: self.viewToDragSnapshotImageView, targetCenterPoint: initialDragPoint, completion: nil) + + dragAndDropManager.groupDefaults.removeObject(forKey: StoreKeys.draggingValidation) + + } + + default: + break + + } + + } + + } + +} diff --git a/SplitViewDragAndDrop/SplitViewDragAndDrop+Utils.swift b/SplitViewDragAndDrop/SplitViewDragAndDrop+Utils.swift new file mode 100755 index 0000000..419d46d --- /dev/null +++ b/SplitViewDragAndDrop/SplitViewDragAndDrop+Utils.swift @@ -0,0 +1,52 @@ +// +// SplitViewDragAndDrop+Utils.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +extension UIView { + + func getSnapshot() -> UIImage? { + + var image: UIImage? + + UIGraphicsBeginImageContextWithOptions(frame.size, false, UIScreen.main.scale) + + if let graphicCurrentContext = UIGraphicsGetCurrentContext() { + + layer.render(in: graphicCurrentContext) + image = UIGraphicsGetImageFromCurrentImageContext() + + } + + UIGraphicsEndImageContext() + + return image + + } + + var windowRelativeFrame: CGRect { + return self.convert(self.bounds, to: nil) + } + +} + +extension CGRect { + + var center: CGPoint { + return CGPoint(x: midX, y: midY) + } + +} + +extension UIWindow { + + static var width: CGFloat { + return UIApplication.shared.delegate?.window??.frame.size.width ?? 0 + } + +} diff --git a/SplitViewDragAndDrop/SplitViewDragAndDrop.swift b/SplitViewDragAndDrop/SplitViewDragAndDrop.swift new file mode 100755 index 0000000..319d860 --- /dev/null +++ b/SplitViewDragAndDrop/SplitViewDragAndDrop.swift @@ -0,0 +1,348 @@ +// +// SplitViewDragAndDrop.swift +// DragAndDropDemo +// +// Created by Mario on 26/05/2017. +// Copyright © 2017 Mario. All rights reserved. +// + +import UIKit + +class SplitViewDragAndDrop: NSObject { + + private struct StoreKeys { + + static let position = "storeKeysPosition" + static let info = "storeKeysInfo" + static let gestureRecognizer = "storeKeysGestureRecognizer" + + struct Position { + static let frame = "storeKeysPositionFrame" + static let initialDragPoint = "storeKeysPositionInitialDragPoint" + } + + struct Info { + static let dragAndDropIdentifier = "storeKeysInfoDragAndDropIdentifier" + static let viewToDragSnapshotImage = "storeKeysInfoViewToDragSnapshotImage" + static let dataToTransfer = "storeKeysInfoDataToTransfer" + } + + struct GestureRecognizer { + static let state = "storeKeysGestureRecognizerState" + static let isTriggeredToRight = "storeKeysGestureRecognizerIsTriggeredToRight" + } + + } + + typealias DraggingUpdateClosure = (_ frame: CGRect, _ image: UIImage?, _ data: Data?) -> Void + typealias DraggingValidationClosure = (_ frame: CGRect, _ image: UIImage?, _ data: Data?) -> Bool + typealias DraggingCompletionClosure = (_ frame: CGRect, _ image: UIImage?, _ data: Data?, _ isValid: Bool) -> Void + + private static var shared: SplitViewDragAndDrop! + + internal var groupDefaults: UserDefaults + private var dragger: Dragger! + + private init(groupIdentifier: String) { + groupDefaults = UserDefaults(suiteName: groupIdentifier)! + } + + private var draggingUpdateClosures = [String: DraggingUpdateClosure]() + private var draggingBeganClosures = [String: DraggingUpdateClosure]() + private var draggingValidationClosures = [String: DraggingValidationClosure]() + private var draggingCompletionClosures = [String: DraggingCompletionClosure]() + private var draggingTargetViews = [String: UIView]() + private var dataToTransfers = [String: Data]() + + private var initialDragPoint = CGPoint.zero + private var viewToDragSnapshotImage: UIImage? + private var draggingViewFrame: CGRect? + private var draggingBeganClosureCalled = false + + private var draggingUpdateClosure: DraggingUpdateClosure? { + get { return draggingUpdateClosures[currentIdentifier] } + set { draggingUpdateClosures[currentIdentifier] = newValue } + } + + internal var currentIdentifier = "" + + internal var draggingBeganClosure: DraggingUpdateClosure? { + get { return draggingBeganClosures[currentIdentifier] } + set { draggingBeganClosures[currentIdentifier] = newValue } + } + internal var draggingValidationClosure: DraggingValidationClosure? { + get { return draggingValidationClosures[currentIdentifier] } + set { draggingValidationClosures[currentIdentifier] = newValue } + } + internal var draggingCompletionClosure: DraggingCompletionClosure? { + get { return draggingCompletionClosures[currentIdentifier] } + set { draggingCompletionClosures[currentIdentifier] = newValue } + } + internal var draggingTargetView: UIView? { + get { return draggingTargetViews[currentIdentifier] } + set { draggingTargetViews[currentIdentifier] = newValue } + } + internal var dataToTransfer: Data? { + get { return dataToTransfers[currentIdentifier] } + set { dataToTransfers[currentIdentifier] = newValue } + } + + internal var viewToDragSnapshotImageView = UIImageView() + + internal func prepareForDraggingUpdate(identifier: String, viewToDragSnapshotImage: UIImage, dataToTransfer: Data?) { + + var draggingUpdateDictionary: [String: Any] = [ + StoreKeys.Info.dragAndDropIdentifier: identifier, + StoreKeys.Info.viewToDragSnapshotImage: viewToDragSnapshotImage + ] + + if let dataToTransfer = dataToTransfer { + draggingUpdateDictionary[StoreKeys.Info.dataToTransfer] = dataToTransfer + } + + groupDefaults.set(NSKeyedArchiver.archivedData(withRootObject: draggingUpdateDictionary), forKey: StoreKeys.info) + groupDefaults.synchronize() + + } + internal func notifyDraggingUpdate(frame: CGRect, initialDragPoint: CGPoint) { + + let draggingUpdateDictionary: [String: Any] = [ + StoreKeys.Position.frame: frame, + StoreKeys.Position.initialDragPoint: initialDragPoint + ] + + groupDefaults.set(NSKeyedArchiver.archivedData(withRootObject: draggingUpdateDictionary), forKey: StoreKeys.position) + groupDefaults.synchronize() + + } + internal func notifyGestureRecognizerUpdate(state: UIGestureRecognizerState, isTriggeredToRight: Bool) { + + let gestureRecognizerDictionary: [String: Any] = [ + StoreKeys.GestureRecognizer.state: state.rawValue, + StoreKeys.GestureRecognizer.isTriggeredToRight: isTriggeredToRight + ] + + groupDefaults.set(NSKeyedArchiver.archivedData(withRootObject: gestureRecognizerDictionary), forKey: StoreKeys.gestureRecognizer) + groupDefaults.synchronize() + + } + + internal func observeDraggingUpdate(onDragUpdate: @escaping DraggingUpdateClosure) { + + draggingUpdateClosure = onDragUpdate + + groupDefaults.addObserver(self, forKeyPath: StoreKeys.position, options: .new, context: nil) + groupDefaults.addObserver(self, forKeyPath: StoreKeys.info, options: .new, context: nil) + groupDefaults.addObserver(self, forKeyPath: StoreKeys.gestureRecognizer, options: .new, context: nil) + + } + + deinit { + + groupDefaults.removeObserver(self, forKeyPath: StoreKeys.position) + groupDefaults.removeObserver(self, forKeyPath: StoreKeys.info) + groupDefaults.removeObserver(self, forKeyPath: StoreKeys.gestureRecognizer) + + } + + internal static func transformXCoordinate(_ value: CGFloat, updateTriggeredToRight: Bool) -> CGFloat { + + return updateTriggeredToRight ? value - UIWindow.width : UIScreen.main.bounds.width - UIWindow.width + value + + } + + internal override func observeValue( + forKeyPath keyPath: String?, of object: Any?, + change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + + guard let keyPath = keyPath, let changedData = change?[.newKey] as? Data else { return } + + switch keyPath { + + case StoreKeys.info: + + if let dragginInfoDictionary = NSKeyedUnarchiver.unarchiveObject(with: changedData) as? [String: Any] { + + viewToDragSnapshotImage = dragginInfoDictionary[StoreKeys.Info.viewToDragSnapshotImage] as? UIImage + currentIdentifier = dragginInfoDictionary[StoreKeys.Info.dragAndDropIdentifier] as? String ?? "" + dataToTransfer = dragginInfoDictionary[StoreKeys.Info.dataToTransfer] as? Data + + } + + case StoreKeys.position: + + if let dragginPositionDictionary = NSKeyedUnarchiver.unarchiveObject(with: changedData) as? [String: Any] { + + draggingViewFrame = dragginPositionDictionary[StoreKeys.Position.frame] as? CGRect ?? .zero + initialDragPoint = dragginPositionDictionary[StoreKeys.Position.initialDragPoint] as? CGPoint ?? .zero + draggingUpdateClosure?(draggingViewFrame ?? .zero, viewToDragSnapshotImage, dataToTransfer) + + } + + case StoreKeys.gestureRecognizer: + + if + let draggingGestureRecognizer = NSKeyedUnarchiver.unarchiveObject(with: changedData) as? [String: Any], + let draggingGestureRecognizerStateRawValue = draggingGestureRecognizer[StoreKeys.GestureRecognizer.state] as? Int, + let draggingGestureRecognizerState = UIGestureRecognizerState(rawValue: draggingGestureRecognizerStateRawValue), + let draggingGestureRecognizerIsTriggerToRight = draggingGestureRecognizer[StoreKeys.GestureRecognizer.isTriggeredToRight] as? Bool + + { + + switch draggingGestureRecognizerState { + + case .began: + + if draggingBeganClosureCalled == false { + draggingBeganClosure?(draggingViewFrame ?? .zero, viewToDragSnapshotImage, dataToTransfer) + draggingBeganClosureCalled = true + } + + case .ended: + + draggingBeganClosureCalled = false + + let draggingValidationResult = draggingValidationClosure?(draggingViewFrame ?? .zero, viewToDragSnapshotImage, dataToTransfer) ?? false + + var initialPoint = self.initialDragPoint + initialPoint.x = SplitViewDragAndDrop.transformXCoordinate(initialPoint.x, updateTriggeredToRight: !draggingGestureRecognizerIsTriggerToRight) + + dragger.notifyDraggingValidationResult(draggingValidationResult, initialDragPoint: initialPoint) + + if draggingViewFrame?.origin.x != 0 && draggingViewFrame?.origin.y != 0 { + + if draggingValidationResult { + + SplitViewDragAndDrop.completeDragging( + isFallBack: false, + draggingView: viewToDragSnapshotImageView, + targetCenterPoint: draggingTargetView?.windowRelativeFrame.center ?? .zero) { + + self.draggingCompletionClosure?( + self.draggingViewFrame ?? .zero, + self.viewToDragSnapshotImage, + self.dataToTransfer, + true + ) + + } + + } else { + + SplitViewDragAndDrop.completeDragging( + isFallBack: true, + draggingView: viewToDragSnapshotImageView, + targetCenterPoint: initialDragPoint) { + + self.draggingCompletionClosure?( + self.draggingViewFrame ?? .zero, + self.viewToDragSnapshotImage, + self.dataToTransfer, + false + ) + + } + + } + + } + + groupDefaults.removeObject(forKey: StoreKeys.gestureRecognizer) + groupDefaults.removeObject(forKey: StoreKeys.info) + groupDefaults.removeObject(forKey: StoreKeys.position) + + default: + break + + } + + } + + default: + break + + } + + } + + internal static func completeDragging(isFallBack: Bool, draggingView: UIView, targetCenterPoint: CGPoint, completion: (() -> Void)?) { + + UIView.animate( + withDuration: 0.3, + animations: { + draggingView.alpha = isFallBack ? 0 : 1 + draggingView.center = targetCenterPoint + }, + completion: { _ in + draggingView.alpha = 1 + draggingView.removeFromSuperview() + completion?() + } + ) + + } + + private func refreshViewToDragSnapshotImageView(frame: CGRect, draggedViewSnapshot: UIImage?) { + + guard let keyWindow = UIApplication.shared.keyWindow else { return } + + viewToDragSnapshotImageView.frame = frame + + if !viewToDragSnapshotImageView.isDescendant(of: keyWindow) { // first time + viewToDragSnapshotImageView.image = draggedViewSnapshot + keyWindow.addSubview(viewToDragSnapshotImageView) + } + + } + + // MARK: - Public interface + + open static func configure(groupIdentifier: String) { + + shared = SplitViewDragAndDrop(groupIdentifier: groupIdentifier) + + shared.dragger = Dragger(dragAndDropManager: shared) + + } + + open static func handleDrag(viewToDrag: UIView, identifier: String, dataToTransfer: Data? = nil) { + + shared.dragger.handleDrag(viewToDrag: viewToDrag, identifier: identifier, dataToTransfer: dataToTransfer) + + } + + open static func addDropObserver( + targetView: UIView, + identifier: String, + draggingBegan: DraggingUpdateClosure?, + draggingValidation: @escaping DraggingValidationClosure, + completion: @escaping DraggingCompletionClosure) { + + DispatchQueue.main.async { + + shared.currentIdentifier = identifier + + shared.draggingTargetView = targetView + shared.draggingBeganClosure = draggingBegan + shared.draggingValidationClosure = draggingValidation + shared.draggingCompletionClosure = completion + + shared.observeDraggingUpdate { frame, image, dataTransfered in + + shared.refreshViewToDragSnapshotImageView(frame: frame, draggedViewSnapshot: image) + + } + + } + + } + + open static func removeDropObserver(withIdentifier identifier: String) { + + shared.dataToTransfers.removeValue(forKey: identifier) + shared.draggingUpdateClosures.removeValue(forKey: identifier) + shared.draggingValidationClosures.removeValue(forKey: identifier) + + } + +}