├── .gitignore ├── LICENSE ├── README.md ├── YJ3DTouch.podspec ├── YJ3DTouch.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── YJ3DTouch ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── SecondViewController.h ├── SecondViewController.m ├── SecondViewController.xib ├── ViewController.h ├── ViewController.m ├── YJ3DTouch │ ├── UIViewController+YJ3DTouch.h │ ├── UIViewController+YJ3DTouch.m │ ├── YJ3DTouchConfig.h │ ├── YJ3DTouchConfig.m │ ├── YJ3DTouchUtil.h │ └── YJ3DTouchUtil.m └── main.m └── tip.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 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 hyman 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 | # YJ3DTouch 2 | YJ3DTouch can easily implement 3D Touch. 3 | 4 | 5 | 6 | ## How to use 7 | 8 | #### First 9 | ```shell 10 | pod 'YJ3DTouch', '~> 1.0' 11 | ``` 12 | ```objective-c 13 | #import "UIViewController+YJ3DTouch.h" 14 | ``` 15 | 16 | #### Active 3D Touch for UITableView 17 | 18 | ```objective-c 19 | [self yj_active3DTouchTable:self.tableView forNavigation:self.navigationController]; 20 | ``` 21 | 22 | > 1, The method will automatic register 3D Touch for each cell. 23 | > 24 | > 2, The table of delegate need implement the "tableView: didSelectRowAtIndexPath:" method. 25 | 26 | 27 | #### Active 3D Touch for UICollectionView 28 | 29 | ```objective-c 30 | [self yj_active3DTouchCollectionView:self.collectionView forNavigation:self.navigationController]; 31 | ``` 32 | 33 | > 1, The method will automatic register 3D Touch for each cell. 34 | > 35 | > 2, The collectionView of delegate need implement the "collectionView: didSelectItemAtIndexPath:" method. 36 | > 37 | > 3, If the collectionView: shouldSelectItemAtIndexPath: method for the UICollectionViewDelegate return NO, at the indexPath of cell will not be registered 3D Touch. 38 | 39 | 40 | #### Active 3D Touch for UIView 41 | 42 | ```objective-c 43 | [self yj_active3DTouchView:self.pushButton 44 | clickTarget:self 45 | clickAction:@selector(pushButtonTap) 46 | argument:nil 47 | forNavigation:self.navigationController]; 48 | ``` 49 | 50 | > 1, If the view is UITableView or UICollectionView, the method will ignore target, action, argument. 51 | > 52 | > 2, You can also use the method to active 3D Touch for UITableViewCell or UICollectionViewCell. However, you can no longer use the "yj_active3DTouchTable:forNavigation:" or "yj_active3DTouchCollectionView:forNavigation:" method. 53 | 54 | 55 | #### Active 3D Touch with action 56 | 57 | ```objective-c 58 | YJ3DTouchConfig *actionItemConfig = [YJ3DTouchConfig new]; 59 | actionItemConfig.navigation = self.navigationController; 60 | actionItemConfig.clickActionTarget = self; 61 | actionItemConfig.clickAction = @selector(actionItemsBtnTap); 62 | 63 | UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"action1" 64 | style:UIPreviewActionStyleDefault 65 | handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { 66 | NSLog(@"action1"); 67 | }]; 68 | UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"action2" 69 | style:UIPreviewActionStyleDefault 70 | handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { 71 | NSLog(@"action2"); 72 | }]; 73 | actionItemConfig.previewActionItems = @[action1, action2]; 74 | [self yj_active3DTouchView:self.actionItemsBtn touchConfig:actionItemConfig]; 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /YJ3DTouch.podspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pod::Spec.new do |s| 4 | s.name = "YJ3DTouch" 5 | s.version = "1.3.0" 6 | s.summary = "Adapt very easily to 3D Touch." 7 | s.description = <<-DESC 8 | YJ3DTouch can easily implement 3D Touch. 9 | DESC 10 | 11 | s.homepage = "https://github.com/Hyman00/YJ3DTouch" 12 | s.license = { :type => "MIT", :file => "LICENSE" } 13 | s.author = { "Hyman00" => "skongoo@163.com" } 14 | 15 | s.platform = :ios 16 | s.platform = :ios, "7.0" 17 | 18 | s.source = { :git => "https://github.com/Hyman00/YJ3DTouch.git", :tag => "#{s.version}" } 19 | 20 | 21 | s.source_files = "YJ3DTouch/YJ3DTouch", "YJ3DTouch/YJ3DTouch/**/*.{h,m}" 22 | 23 | s.requires_arc = true 24 | 25 | end 26 | -------------------------------------------------------------------------------- /YJ3DTouch.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E425886C1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = E425886B1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.m */; }; 11 | E48B19BB1E8D5CBB00E1CB78 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E48B19BA1E8D5CBB00E1CB78 /* main.m */; }; 12 | E48B19BE1E8D5CBB00E1CB78 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E48B19BD1E8D5CBB00E1CB78 /* AppDelegate.m */; }; 13 | E48B19C11E8D5CBB00E1CB78 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E48B19C01E8D5CBB00E1CB78 /* ViewController.m */; }; 14 | E48B19C41E8D5CBB00E1CB78 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E48B19C21E8D5CBB00E1CB78 /* Main.storyboard */; }; 15 | E48B19C61E8D5CBB00E1CB78 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E48B19C51E8D5CBB00E1CB78 /* Assets.xcassets */; }; 16 | E48B19C91E8D5CBB00E1CB78 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E48B19C71E8D5CBB00E1CB78 /* LaunchScreen.storyboard */; }; 17 | E4F4AA771E8FB90500A191A7 /* YJ3DTouchUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F4AA761E8FB90500A191A7 /* YJ3DTouchUtil.m */; }; 18 | E4F5E0CF1E8EA7CF00C7252C /* YJ3DTouchConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F5E0CE1E8EA7CF00C7252C /* YJ3DTouchConfig.m */; }; 19 | E4F5E0ED1E8EBEC000C7252C /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F5E0EB1E8EBEC000C7252C /* SecondViewController.m */; }; 20 | E4F5E0EE1E8EBEC000C7252C /* SecondViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E4F5E0EC1E8EBEC000C7252C /* SecondViewController.xib */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | E425886A1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+YJ3DTouch.h"; sourceTree = ""; }; 25 | E425886B1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+YJ3DTouch.m"; sourceTree = ""; }; 26 | E48B19B61E8D5CBB00E1CB78 /* YJ3DTouch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YJ3DTouch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | E48B19BA1E8D5CBB00E1CB78 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 28 | E48B19BC1E8D5CBB00E1CB78 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | E48B19BD1E8D5CBB00E1CB78 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | E48B19BF1E8D5CBB00E1CB78 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | E48B19C01E8D5CBB00E1CB78 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | E48B19C31E8D5CBB00E1CB78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | E48B19C51E8D5CBB00E1CB78 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | E48B19C81E8D5CBB00E1CB78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | E48B19CA1E8D5CBB00E1CB78 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | E4F4AA751E8FB90500A191A7 /* YJ3DTouchUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YJ3DTouchUtil.h; sourceTree = ""; }; 37 | E4F4AA761E8FB90500A191A7 /* YJ3DTouchUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YJ3DTouchUtil.m; sourceTree = ""; }; 38 | E4F5E0CD1E8EA7CF00C7252C /* YJ3DTouchConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YJ3DTouchConfig.h; sourceTree = ""; }; 39 | E4F5E0CE1E8EA7CF00C7252C /* YJ3DTouchConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YJ3DTouchConfig.m; sourceTree = ""; }; 40 | E4F5E0EA1E8EBEC000C7252C /* SecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; }; 41 | E4F5E0EB1E8EBEC000C7252C /* SecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; }; 42 | E4F5E0EC1E8EBEC000C7252C /* SecondViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SecondViewController.xib; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | E48B19B31E8D5CBB00E1CB78 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | E42588691E8D6EBF00B9E131 /* YJ3DTouch */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | E425886A1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.h */, 60 | E425886B1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.m */, 61 | E4F5E0CD1E8EA7CF00C7252C /* YJ3DTouchConfig.h */, 62 | E4F5E0CE1E8EA7CF00C7252C /* YJ3DTouchConfig.m */, 63 | E4F4AA751E8FB90500A191A7 /* YJ3DTouchUtil.h */, 64 | E4F4AA761E8FB90500A191A7 /* YJ3DTouchUtil.m */, 65 | ); 66 | path = YJ3DTouch; 67 | sourceTree = ""; 68 | }; 69 | E48B19AD1E8D5CBB00E1CB78 = { 70 | isa = PBXGroup; 71 | children = ( 72 | E48B19B81E8D5CBB00E1CB78 /* YJ3DTouch */, 73 | E48B19B71E8D5CBB00E1CB78 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | E48B19B71E8D5CBB00E1CB78 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | E48B19B61E8D5CBB00E1CB78 /* YJ3DTouch.app */, 81 | ); 82 | name = Products; 83 | sourceTree = ""; 84 | }; 85 | E48B19B81E8D5CBB00E1CB78 /* YJ3DTouch */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | E42588691E8D6EBF00B9E131 /* YJ3DTouch */, 89 | E48B19BC1E8D5CBB00E1CB78 /* AppDelegate.h */, 90 | E48B19BD1E8D5CBB00E1CB78 /* AppDelegate.m */, 91 | E48B19BF1E8D5CBB00E1CB78 /* ViewController.h */, 92 | E48B19C01E8D5CBB00E1CB78 /* ViewController.m */, 93 | E4F5E0EA1E8EBEC000C7252C /* SecondViewController.h */, 94 | E4F5E0EB1E8EBEC000C7252C /* SecondViewController.m */, 95 | E4F5E0EC1E8EBEC000C7252C /* SecondViewController.xib */, 96 | E48B19C21E8D5CBB00E1CB78 /* Main.storyboard */, 97 | E48B19C51E8D5CBB00E1CB78 /* Assets.xcassets */, 98 | E48B19C71E8D5CBB00E1CB78 /* LaunchScreen.storyboard */, 99 | E48B19CA1E8D5CBB00E1CB78 /* Info.plist */, 100 | E48B19B91E8D5CBB00E1CB78 /* Supporting Files */, 101 | ); 102 | path = YJ3DTouch; 103 | sourceTree = ""; 104 | }; 105 | E48B19B91E8D5CBB00E1CB78 /* Supporting Files */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | E48B19BA1E8D5CBB00E1CB78 /* main.m */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | E48B19B51E8D5CBB00E1CB78 /* YJ3DTouch */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = E48B19CD1E8D5CBB00E1CB78 /* Build configuration list for PBXNativeTarget "YJ3DTouch" */; 119 | buildPhases = ( 120 | E48B19B21E8D5CBB00E1CB78 /* Sources */, 121 | E48B19B31E8D5CBB00E1CB78 /* Frameworks */, 122 | E48B19B41E8D5CBB00E1CB78 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = YJ3DTouch; 129 | productName = YJ3DTouch; 130 | productReference = E48B19B61E8D5CBB00E1CB78 /* YJ3DTouch.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | E48B19AE1E8D5CBB00E1CB78 /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastUpgradeCheck = 0820; 140 | ORGANIZATIONNAME = hyman; 141 | TargetAttributes = { 142 | E48B19B51E8D5CBB00E1CB78 = { 143 | CreatedOnToolsVersion = 8.2; 144 | ProvisioningStyle = Manual; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = E48B19B11E8D5CBB00E1CB78 /* Build configuration list for PBXProject "YJ3DTouch" */; 149 | compatibilityVersion = "Xcode 3.2"; 150 | developmentRegion = English; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = E48B19AD1E8D5CBB00E1CB78; 157 | productRefGroup = E48B19B71E8D5CBB00E1CB78 /* Products */; 158 | projectDirPath = ""; 159 | projectRoot = ""; 160 | targets = ( 161 | E48B19B51E8D5CBB00E1CB78 /* YJ3DTouch */, 162 | ); 163 | }; 164 | /* End PBXProject section */ 165 | 166 | /* Begin PBXResourcesBuildPhase section */ 167 | E48B19B41E8D5CBB00E1CB78 /* Resources */ = { 168 | isa = PBXResourcesBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | E4F5E0EE1E8EBEC000C7252C /* SecondViewController.xib in Resources */, 172 | E48B19C91E8D5CBB00E1CB78 /* LaunchScreen.storyboard in Resources */, 173 | E48B19C61E8D5CBB00E1CB78 /* Assets.xcassets in Resources */, 174 | E48B19C41E8D5CBB00E1CB78 /* Main.storyboard in Resources */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXResourcesBuildPhase section */ 179 | 180 | /* Begin PBXSourcesBuildPhase section */ 181 | E48B19B21E8D5CBB00E1CB78 /* Sources */ = { 182 | isa = PBXSourcesBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | E425886C1E8D6ECC00B9E131 /* UIViewController+YJ3DTouch.m in Sources */, 186 | E48B19C11E8D5CBB00E1CB78 /* ViewController.m in Sources */, 187 | E48B19BE1E8D5CBB00E1CB78 /* AppDelegate.m in Sources */, 188 | E4F5E0ED1E8EBEC000C7252C /* SecondViewController.m in Sources */, 189 | E48B19BB1E8D5CBB00E1CB78 /* main.m in Sources */, 190 | E4F4AA771E8FB90500A191A7 /* YJ3DTouchUtil.m in Sources */, 191 | E4F5E0CF1E8EA7CF00C7252C /* YJ3DTouchConfig.m in Sources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXSourcesBuildPhase section */ 196 | 197 | /* Begin PBXVariantGroup section */ 198 | E48B19C21E8D5CBB00E1CB78 /* Main.storyboard */ = { 199 | isa = PBXVariantGroup; 200 | children = ( 201 | E48B19C31E8D5CBB00E1CB78 /* Base */, 202 | ); 203 | name = Main.storyboard; 204 | sourceTree = ""; 205 | }; 206 | E48B19C71E8D5CBB00E1CB78 /* LaunchScreen.storyboard */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | E48B19C81E8D5CBB00E1CB78 /* Base */, 210 | ); 211 | name = LaunchScreen.storyboard; 212 | sourceTree = ""; 213 | }; 214 | /* End PBXVariantGroup section */ 215 | 216 | /* Begin XCBuildConfiguration section */ 217 | E48B19CB1E8D5CBB00E1CB78 /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | ALWAYS_SEARCH_USER_PATHS = NO; 221 | CLANG_ANALYZER_NONNULL = YES; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 235 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 236 | CLANG_WARN_UNREACHABLE_CODE = YES; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 239 | COPY_PHASE_STRIP = NO; 240 | DEBUG_INFORMATION_FORMAT = dwarf; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | ENABLE_TESTABILITY = YES; 243 | GCC_C_LANGUAGE_STANDARD = gnu99; 244 | GCC_DYNAMIC_NO_PIC = NO; 245 | GCC_NO_COMMON_BLOCKS = YES; 246 | GCC_OPTIMIZATION_LEVEL = 0; 247 | GCC_PREPROCESSOR_DEFINITIONS = ( 248 | "DEBUG=1", 249 | "$(inherited)", 250 | ); 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 258 | MTL_ENABLE_DEBUG_INFO = YES; 259 | ONLY_ACTIVE_ARCH = YES; 260 | SDKROOT = iphoneos; 261 | TARGETED_DEVICE_FAMILY = "1,2"; 262 | }; 263 | name = Debug; 264 | }; 265 | E48B19CC1E8D5CBB00E1CB78 /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 278 | CLANG_WARN_EMPTY_BODY = YES; 279 | CLANG_WARN_ENUM_CONVERSION = YES; 280 | CLANG_WARN_INFINITE_RECURSION = YES; 281 | CLANG_WARN_INT_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 287 | COPY_PHASE_STRIP = NO; 288 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 289 | ENABLE_NS_ASSERTIONS = NO; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | GCC_C_LANGUAGE_STANDARD = gnu99; 292 | GCC_NO_COMMON_BLOCKS = YES; 293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 294 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 295 | GCC_WARN_UNDECLARED_SELECTOR = YES; 296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 297 | GCC_WARN_UNUSED_FUNCTION = YES; 298 | GCC_WARN_UNUSED_VARIABLE = YES; 299 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 300 | MTL_ENABLE_DEBUG_INFO = NO; 301 | SDKROOT = iphoneos; 302 | TARGETED_DEVICE_FAMILY = "1,2"; 303 | VALIDATE_PRODUCT = YES; 304 | }; 305 | name = Release; 306 | }; 307 | E48B19CE1E8D5CBB00E1CB78 /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 311 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 312 | DEVELOPMENT_TEAM = ""; 313 | INFOPLIST_FILE = YJ3DTouch/Info.plist; 314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 315 | PRODUCT_BUNDLE_IDENTIFIER = hyman.YJ3DTouch; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | }; 318 | name = Debug; 319 | }; 320 | E48B19CF1E8D5CBB00E1CB78 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 325 | DEVELOPMENT_TEAM = ""; 326 | INFOPLIST_FILE = YJ3DTouch/Info.plist; 327 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 328 | PRODUCT_BUNDLE_IDENTIFIER = hyman.YJ3DTouch; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | }; 331 | name = Release; 332 | }; 333 | /* End XCBuildConfiguration section */ 334 | 335 | /* Begin XCConfigurationList section */ 336 | E48B19B11E8D5CBB00E1CB78 /* Build configuration list for PBXProject "YJ3DTouch" */ = { 337 | isa = XCConfigurationList; 338 | buildConfigurations = ( 339 | E48B19CB1E8D5CBB00E1CB78 /* Debug */, 340 | E48B19CC1E8D5CBB00E1CB78 /* Release */, 341 | ); 342 | defaultConfigurationIsVisible = 0; 343 | defaultConfigurationName = Release; 344 | }; 345 | E48B19CD1E8D5CBB00E1CB78 /* Build configuration list for PBXNativeTarget "YJ3DTouch" */ = { 346 | isa = XCConfigurationList; 347 | buildConfigurations = ( 348 | E48B19CE1E8D5CBB00E1CB78 /* Debug */, 349 | E48B19CF1E8D5CBB00E1CB78 /* Release */, 350 | ); 351 | defaultConfigurationIsVisible = 0; 352 | defaultConfigurationName = Release; 353 | }; 354 | /* End XCConfigurationList section */ 355 | }; 356 | rootObject = E48B19AE1E8D5CBB00E1CB78 /* Project object */; 357 | } 358 | -------------------------------------------------------------------------------- /YJ3DTouch.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YJ3DTouch/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/30. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /YJ3DTouch/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/30. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /YJ3DTouch/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /YJ3DTouch/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 | 27 | 28 | -------------------------------------------------------------------------------- /YJ3DTouch/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 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 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 | 107 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /YJ3DTouch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /YJ3DTouch/SecondViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/4/1. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondViewController : UIViewController 12 | 13 | @property (nonatomic, copy) NSString *detail; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /YJ3DTouch/SecondViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/4/1. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "SecondViewController.h" 10 | #import "UIViewController+YJ3DTouch.h" 11 | 12 | @interface SecondViewController () 13 | @property (nonatomic, weak) IBOutlet UILabel *label; 14 | @property (nonatomic, weak) IBOutlet UIButton *closeButton; 15 | @end 16 | 17 | @implementation SecondViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view from its nib. 22 | 23 | self.label.text = self.detail; 24 | 25 | [self.closeButton addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside]; 26 | } 27 | 28 | - (void)viewWillAppear:(BOOL)animated { 29 | [super viewWillAppear:animated]; 30 | 31 | if (self.yj_3DTouchStatus == YJ3DTouchStatus_Previewing) { 32 | self.closeButton.hidden = YES; 33 | } else { 34 | self.closeButton.hidden = self.presentingViewController == nil; 35 | } 36 | } 37 | 38 | - (void)setDetail:(NSString *)detail { 39 | _detail = [detail copy]; 40 | self.label.text = _detail; 41 | } 42 | 43 | - (void)close { 44 | [self dismissViewControllerAnimated:YES completion:nil]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /YJ3DTouch/SecondViewController.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 | 33 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /YJ3DTouch/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/30. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /YJ3DTouch/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/30. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SecondViewController.h" 11 | #import "UIViewController+YJ3DTouch.h" 12 | 13 | @interface ViewController () 14 | @property (nonatomic, weak) IBOutlet UITableView *tableView; 15 | @property (nonatomic, weak) IBOutlet UICollectionView *collectionView; 16 | @property (nonatomic, weak) IBOutlet UIButton *pushButton; 17 | @property (nonatomic, weak) IBOutlet UIButton *presentButton; 18 | @property (nonatomic, weak) IBOutlet UIView *customView; 19 | @property (nonatomic, weak) IBOutlet UIButton *actionItemsBtn; 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | // pushButton 28 | [self.pushButton addTarget:self action:@selector(pushButtonTap) forControlEvents:UIControlEventTouchUpInside]; 29 | 30 | [self yj_active3DTouchView:self.pushButton 31 | clickTarget:self 32 | clickAction:@selector(pushButtonTap) 33 | argument:nil 34 | forNavigation:self.navigationController]; 35 | 36 | // presentButton 37 | [self.presentButton addTarget:self 38 | action:@selector(presentButtonTap:) 39 | forControlEvents:UIControlEventTouchUpInside]; 40 | 41 | YJ3DTouchConfig *presentConfig = [YJ3DTouchConfig new]; 42 | presentConfig.clickActionTarget = self; 43 | presentConfig.clickAction = @selector(presentButtonTap:); 44 | presentConfig.argument = self.presentButton; 45 | presentConfig.presentingViewController = self.navigationController; 46 | [self yj_active3DTouchView:self.presentButton touchConfig:presentConfig]; 47 | 48 | // customView 49 | UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(customViewTap)]; 50 | [self.customView addGestureRecognizer:tapG]; 51 | 52 | YJ3DTouchConfig *customConfig = [YJ3DTouchConfig new]; 53 | customConfig.navigation = self.navigationController; 54 | customConfig.clickActionTarget = self; 55 | customConfig.clickAction = @selector(customViewTap); 56 | [self yj_active3DTouchView:self.customView touchConfig:customConfig]; 57 | 58 | 59 | // actionItemsBtn 60 | [self.actionItemsBtn addTarget:self action:@selector(actionItemsBtnTap) forControlEvents:UIControlEventTouchUpInside]; 61 | YJ3DTouchConfig *actionItemConfig = [YJ3DTouchConfig new]; 62 | actionItemConfig.navigation = self.navigationController; 63 | actionItemConfig.clickActionTarget = self; 64 | actionItemConfig.clickAction = @selector(actionItemsBtnTap); 65 | 66 | UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"action1" 67 | style:UIPreviewActionStyleDefault 68 | handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { 69 | NSLog(@"action1"); 70 | }]; 71 | UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"action2" 72 | style:UIPreviewActionStyleDefault 73 | handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { 74 | NSLog(@"action2"); 75 | }]; 76 | actionItemConfig.previewActionItems = @[action1, action2]; 77 | [self yj_active3DTouchView:self.actionItemsBtn touchConfig:actionItemConfig]; 78 | 79 | 80 | // tableView 81 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellID"]; 82 | [self yj_active3DTouchTable:self.tableView forNavigation:self.navigationController]; 83 | 84 | 85 | // collectionView 86 | [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellID"]; 87 | [self yj_active3DTouchCollectionView:self.collectionView forNavigation:self.navigationController]; 88 | 89 | 90 | self.yj_3DTouchTriggerBlock = ^BOOL(YJ3DTouchActionStatus status, UIView *sourceView) { 91 | NSLog(@"status: %ld, sourceView: %@", status, sourceView); 92 | return YES; 93 | }; 94 | } 95 | 96 | - (void)customViewTap { 97 | SecondViewController *vc = [SecondViewController new]; 98 | vc.detail = @"customViewTap"; 99 | [self.navigationController pushViewController:vc animated:YES]; 100 | } 101 | 102 | - (void)pushButtonTap { 103 | SecondViewController *vc = [SecondViewController new]; 104 | vc.detail = @"PushButton"; 105 | [self.navigationController pushViewController:vc animated:YES]; 106 | } 107 | 108 | - (void)actionItemsBtnTap { 109 | SecondViewController *vc = [SecondViewController new]; 110 | vc.detail = @"actionItemsBtnTap"; 111 | [self.navigationController pushViewController:vc animated:YES]; 112 | } 113 | 114 | - (void)presentButtonTap:(UIButton *)btn { 115 | SecondViewController *vc = [SecondViewController new]; 116 | vc.detail = @"presentButtonTap"; 117 | [self.navigationController presentViewController:vc animated:YES completion:nil]; 118 | } 119 | 120 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 121 | return 20; 122 | } 123 | 124 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 125 | { 126 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID" forIndexPath:indexPath]; 127 | cell.textLabel.text = [NSString stringWithFormat:@"cell_%ld", indexPath.row]; 128 | return cell; 129 | } 130 | 131 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 132 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 133 | 134 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 135 | SecondViewController *vc = [SecondViewController new]; 136 | vc.detail = cell.textLabel.text; 137 | [self.navigationController pushViewController:vc animated:YES]; 138 | } 139 | 140 | #pragma mark - UICollectionViewDelegate 141 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 142 | return 20; 143 | } 144 | 145 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 146 | cellForItemAtIndexPath:(NSIndexPath *)indexPath { 147 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellID" forIndexPath:indexPath]; 148 | cell.contentView.backgroundColor = [self private_randomColor]; 149 | 150 | return cell; 151 | } 152 | 153 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 154 | SecondViewController *vc = [SecondViewController new]; 155 | 156 | UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; 157 | 158 | vc.view.backgroundColor = cell.contentView.backgroundColor; 159 | 160 | [self.navigationController pushViewController:vc animated:YES]; 161 | } 162 | 163 | #pragma mark - Private 164 | - (UIColor *)private_randomColor { 165 | CGFloat hue = arc4random() % 100 / 100.0; 166 | CGFloat saturation = (arc4random() % 50 / 100) + 0.5; 167 | CGFloat brightness = (arc4random() % 50 / 100) + 0.5; 168 | 169 | return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/UIViewController+YJ3DTouch.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+YJ3DTouch.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/31. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YJ3DTouchConfig.h" 11 | 12 | typedef NS_ENUM(NSUInteger, YJ3DViewControllerAppear) { 13 | YJ3DViewControllerAppear_None = 0, 14 | YJ3DViewControllerAppear_WillAppear, 15 | YJ3DViewControllerAppear_DidAppear, 16 | YJ3DViewControllerAppear_WillDisappear, 17 | YJ3DViewControllerAppear_DidDisappear 18 | }; 19 | 20 | typedef NS_ENUM(NSUInteger, YJ3DTouchStatus) { 21 | YJ3DTouchStatus_None = 0, 22 | YJ3DTouchStatus_Prepared, 23 | YJ3DTouchStatus_Previewing, 24 | YJ3DTouchStatus_Poped 25 | }; 26 | 27 | typedef NS_ENUM(NSUInteger, YJ3DTouchActionStatus) { 28 | YJ3DTouchActionStatus_None = 0, 29 | YJ3DTouchActionStatus_WillPeek, 30 | YJ3DTouchActionStatus_DidPeek, 31 | YJ3DTouchActionStatus_WillPop, 32 | YJ3DTouchActionStatus_DidPop, 33 | YJ3DTouchActionStatus_Cancel 34 | }; 35 | 36 | extern NSString * const YJ3DTouchActionNotification; 37 | extern NSString * const YJ3DTouchActionUserInfoPageKey; 38 | extern NSString * const YJ3DTouchActionUserInfoStatusKey; 39 | 40 | /** 41 | * @brief It will be called when the sourceView will peek or pop. 42 | * If the block return NO, then the 3D Touch will lose efficacy. 43 | (It only takes effect when status is YJ3DTouchStatus_WillPeek and YJ3DTouchStatus_WillPop) 44 | * 45 | * @param sourceView A source view, in a previewing view controller’s view hierarchy, responds to a forceful touch by the use 46 | */ 47 | typedef BOOL(^YJ3DTouchTriggerBlock) (YJ3DTouchActionStatus status, UIView *sourceView); 48 | 49 | 50 | @interface UIViewController (YJ3DTouch) 51 | 52 | @property (nonatomic, assign, readonly) YJ3DViewControllerAppear yj_appearStatus; 53 | 54 | @property (nonatomic, assign, readonly) YJ3DTouchStatus yj_3DTouchStatus; 55 | 56 | @property (nonatomic, assign, readonly) YJ3DTouchActionStatus yj_3DTouchActionStatus; 57 | @property (nonatomic, copy) YJ3DTouchTriggerBlock yj_3DTouchTriggerBlock; 58 | 59 | /** 60 | * @brief active 3D Touch for UITableView 61 | * 62 | * The method will automatic register 3D Touch for each cell. 63 | * 64 | * @note 65 | * Need to implement the tableView: didSelectRowAtIndexPath: method for the UITableViewDelegate 66 | * 67 | */ 68 | - (void)yj_active3DTouchTable:(UITableView *)tableView forNavigation:(UINavigationController *)navigation; 69 | 70 | /** 71 | * @brief active 3D Touch for UICollectionView 72 | * 73 | * The method will automatic register 3D Touch for each cell. 74 | * 75 | * @note 76 | * 1, Need to implement the collectionView: didSelectItemAtIndexPath: for the UICollectionViewDelegate 77 | * 78 | * 2, If the collectionView: shouldSelectItemAtIndexPath: method for the UICollectionViewDelegate return NO, at the indexPath of cell will not be registered 3D Touch; 79 | * 80 | */ 81 | - (void)yj_active3DTouchCollectionView:(UICollectionView *)collectionView forNavigation:(UINavigationController *)navigation; 82 | 83 | /** 84 | * @brief active 3D Touch for view 85 | * 86 | * @param target the responder of the click 87 | * @param action the action of the click 88 | * @param argument an object that is the sole argument of the action 89 | * 90 | * @note 91 | * If the view is UITableView or UICollectionView, the method will ignore target, action, argument 92 | */ 93 | - (void)yj_active3DTouchView:(UIView *)view 94 | clickTarget:(NSObject *)target 95 | clickAction:(SEL)action 96 | argument:(id)argument 97 | forNavigation:(UINavigationController *)navigation; 98 | 99 | - (void)yj_active3DTouchView:(UIView *)view touchConfig:(YJ3DTouchConfig *)touchConfig; 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/UIViewController+YJ3DTouch.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+YJ3DTouch.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/31. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+YJ3DTouch.h" 10 | #import "YJ3DTouchUtil.h" 11 | #import 12 | 13 | #pragma mark - YJ3DTouch_VC 14 | @interface UIViewController (YJ3DTouch_VC) 15 | @end 16 | 17 | @implementation UIViewController (YJ3DTouch_VC) 18 | 19 | - (UIViewController *)yj3d_private_detailVC { 20 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 21 | } 22 | 23 | - (void)yj3d_private_setDetailVC:(UIViewController *)detailVC { 24 | [YJ3DTouchUtil setAssociatedObject:detailVC withKey:@selector(yj3d_private_detailVC) policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC forTarget:self]; 25 | } 26 | 27 | - (UIViewController *)yj3d_private_hostVC { 28 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 29 | } 30 | 31 | - (void)yj3d_private_setHostVC:(UIViewController *)hostVC { 32 | [YJ3DTouchUtil setAssociatedObject:hostVC withKey:@selector(yj3d_private_hostVC) policy:OBJC_ASSOCIATION_ASSIGN forTarget:self]; 33 | } 34 | 35 | - (BOOL)yj3d_private_activePresent3DTouch { 36 | return [[YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self] boolValue]; 37 | } 38 | 39 | - (void)yj3d_private_setActivePresent3DTouch:(BOOL)active { 40 | [YJ3DTouchUtil setAssociatedObject:@(active) withKey:@selector(yj3d_private_activePresent3DTouch) policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC forTarget:self]; 41 | } 42 | 43 | - (void)yj3d_private_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion 44 | { 45 | if ([self yj3d_private_activePresent3DTouch]) { 46 | [self yj3d_private_setActivePresent3DTouch:NO]; 47 | [self yj3d_private_setDetailVC:viewControllerToPresent]; 48 | return; 49 | } 50 | 51 | [self yj3d_private_presentViewController:viewControllerToPresent animated:flag completion:completion]; 52 | } 53 | 54 | @end 55 | 56 | #pragma mark - YJ3DTouch_Nav 57 | @interface UINavigationController (YJ3DTouch_Nav) 58 | @end 59 | 60 | @implementation UINavigationController (YJ3DTouch_Nav) 61 | 62 | - (void)yj3d_private_pushViewController:(UIViewController *)viewController animated:(BOOL)animated { 63 | if ([self yj3d_private_activePush3DTouch]) { 64 | [self yj3d_private_setActivePush3DTouch:NO]; 65 | [self yj3d_private_setDetailVC:viewController]; 66 | return; 67 | } 68 | 69 | // Handle the problem of pushing the same viewController repeatedly 70 | if (viewController.yj_3DTouchStatus != YJ3DTouchStatus_None 71 | && viewController.yj_3DTouchStatus != YJ3DTouchStatus_Poped) { 72 | return; 73 | } 74 | 75 | [self yj3d_private_pushViewController:viewController animated:animated]; 76 | } 77 | 78 | - (BOOL)yj3d_private_activePush3DTouch { 79 | return [[YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self] boolValue]; 80 | } 81 | 82 | - (void)yj3d_private_setActivePush3DTouch:(BOOL)active { 83 | [YJ3DTouchUtil setAssociatedObject:@(active) withKey:@selector(yj3d_private_activePush3DTouch) policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC forTarget:self]; 84 | } 85 | 86 | @end 87 | 88 | #pragma mark - YJ3DTouch_View 89 | @interface UIView (YJ3DTouch_View) 90 | @end 91 | 92 | @implementation UIView (YJ3DTouch_View) 93 | 94 | - (UIViewController *)yj3d_private_previewDelegateController { 95 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 96 | } 97 | 98 | - (void)yj3d_private_setPreviewDelegateController:(UIViewController *)viewController { 99 | [YJ3DTouchUtil setAssociatedObject:viewController withKey:@selector(yj3d_private_previewDelegateController) policy:OBJC_ASSOCIATION_ASSIGN forTarget:self]; 100 | } 101 | 102 | - (YJ3DTouchConfig *)yj3d_private_3DTouchConfig { 103 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 104 | } 105 | 106 | - (void)yj3d_private_set3DTouchConfig:(YJ3DTouchConfig *)config { 107 | [YJ3DTouchUtil setAssociatedObject:config withKey:@selector(yj3d_private_3DTouchConfig) policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC forTarget:self]; 108 | } 109 | 110 | - (BOOL)yj3d_private_hasRegistered3DTouch { 111 | return [[YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self] boolValue]; 112 | } 113 | 114 | - (void)yj3d_private_setHasRegistered3DTouch:(BOOL)registered { 115 | [YJ3DTouchUtil setAssociatedObject:@(registered) withKey:@selector(yj3d_private_hasRegistered3DTouch) policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC forTarget:self]; 116 | } 117 | 118 | @end 119 | 120 | #pragma mark - YJ3DTouch_Cell 121 | @interface UITableViewCell (YJ3DTouch_Cell) 122 | @end 123 | 124 | @implementation UITableViewCell (YJ3DTouch_Cell) 125 | 126 | - (UITableView *)yj3d_private_cell_tableView { 127 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 128 | } 129 | 130 | - (void)yj3d_private_cell_setTableView:(UITableView *)tableView { 131 | [YJ3DTouchUtil setAssociatedObject:tableView withKey:@selector(yj3d_private_cell_tableView) policy:OBJC_ASSOCIATION_ASSIGN forTarget:self]; 132 | } 133 | 134 | @end 135 | 136 | #pragma mark - YJ3DTouch_CollectionViewCell 137 | @interface UICollectionViewCell (YJ3DTouch_CollectionViewCell) 138 | @end 139 | 140 | @implementation UICollectionViewCell (YJ3DTouch_CollectionViewCell) 141 | 142 | - (UICollectionView *)yj3d_private_cell_CollectionView { 143 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 144 | } 145 | 146 | - (void)yj3d_private_cell_setCollectionView:(UICollectionView *)collectionView { 147 | [YJ3DTouchUtil setAssociatedObject:collectionView withKey:@selector(yj3d_private_cell_CollectionView) policy:OBJC_ASSOCIATION_ASSIGN forTarget:self]; 148 | } 149 | 150 | @end 151 | 152 | static BOOL kYJ3DTouch_VC_Prepared = NO; 153 | NSString * const YJ3DTouchActionNotification = @"YJ3DTouchActionNotification"; 154 | NSString * const YJ3DTouchActionUserInfoPageKey = @"actionPage"; 155 | NSString * const YJ3DTouchActionUserInfoStatusKey = @"actionStatus"; 156 | 157 | #pragma mark - UIViewController YJ3DTouch 158 | @implementation UIViewController (YJ3DTouch) 159 | 160 | + (void)load { 161 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(viewDidLoad)), 162 | class_getInstanceMethod(self, @selector(yj3d_private_viewDidLoad))); 163 | 164 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(viewWillAppear:)), 165 | class_getInstanceMethod(self, @selector(yj3d_private_viewWillAppear:))); 166 | 167 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(viewDidAppear:)), 168 | class_getInstanceMethod(self, @selector(yj3d_private_viewDidAppear:))); 169 | 170 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(viewWillDisappear:)), 171 | class_getInstanceMethod(self, @selector(yj3d_private_viewWillDisappear:))); 172 | 173 | method_exchangeImplementations(class_getInstanceMethod(self, @selector(viewDidDisappear:)), 174 | class_getInstanceMethod(self, @selector(yj3d_private_viewDidDisappear:))); 175 | } 176 | 177 | #pragma mark - Getter & Setter 178 | - (YJ3DTouchStatus)yj_3DTouchStatus { 179 | return [objc_getAssociatedObject(self, _cmd) unsignedIntegerValue]; 180 | } 181 | 182 | - (void)setYj_3DTouchStatus:(YJ3DTouchStatus)yj_3DTouchStatus { 183 | objc_setAssociatedObject(self, @selector(yj_3DTouchStatus), @(yj_3DTouchStatus), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 184 | } 185 | 186 | - (YJ3DTouchTriggerBlock)yj_3DTouchTriggerBlock { 187 | return objc_getAssociatedObject(self, _cmd); 188 | } 189 | 190 | - (void)setYj_3DTouchTriggerBlock:(YJ3DTouchTriggerBlock)yj_3DTouchTriggerBlock { 191 | objc_setAssociatedObject(self, @selector(yj_3DTouchTriggerBlock), yj_3DTouchTriggerBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 192 | } 193 | 194 | - (YJ3DViewControllerAppear)yj_appearStatus { 195 | return [objc_getAssociatedObject(self, _cmd) unsignedIntegerValue]; 196 | } 197 | 198 | - (void)setYj_appearStatus:(YJ3DViewControllerAppear)yj_appearStatus { 199 | objc_setAssociatedObject(self, @selector(yj_appearStatus), @(yj_appearStatus), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 200 | } 201 | 202 | - (YJ3DTouchActionStatus)yj_3DTouchActionStatus { 203 | return [objc_getAssociatedObject(self, _cmd) unsignedIntegerValue]; 204 | } 205 | 206 | - (void)setYj_3DTouchActionStatus:(YJ3DTouchActionStatus)yj_3DTouchActionStatus { 207 | objc_setAssociatedObject(self, @selector(yj_3DTouchActionStatus), @(yj_3DTouchActionStatus), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 208 | 209 | UIViewController *hostVC = self; 210 | 211 | switch (yj_3DTouchActionStatus) { 212 | case YJ3DTouchActionStatus_WillPeek: 213 | case YJ3DTouchActionStatus_DidPeek: 214 | case YJ3DTouchActionStatus_WillPop: 215 | case YJ3DTouchActionStatus_DidPop: 216 | case YJ3DTouchActionStatus_Cancel: { 217 | BOOL canContinue = YES; 218 | if (hostVC.yj_3DTouchTriggerBlock) { 219 | canContinue = hostVC.yj_3DTouchTriggerBlock(yj_3DTouchActionStatus, hostVC.yj3d_private_currentPreviewingView); 220 | } 221 | [hostVC yj3d_private_update3DTouchActionCanContinue:canContinue]; 222 | } 223 | break; 224 | default: 225 | break; 226 | } 227 | 228 | [[NSNotificationCenter defaultCenter] postNotificationName:YJ3DTouchActionNotification 229 | object:nil 230 | userInfo:@{YJ3DTouchActionUserInfoPageKey: self, 231 | YJ3DTouchActionUserInfoStatusKey: @(yj_3DTouchActionStatus)}]; 232 | } 233 | 234 | #pragma mark - Public 235 | - (void)yj_active3DTouchTable:(UITableView *)tableView forNavigation:(UINavigationController *)navigation 236 | { 237 | [self yj_active3DTouchView:tableView 238 | clickTarget:nil 239 | clickAction:nil 240 | argument:nil 241 | forNavigation:navigation]; 242 | } 243 | 244 | - (void)yj_active3DTouchCollectionView:(UICollectionView *)collectionView forNavigation:(UINavigationController *)navigation 245 | { 246 | [self yj_active3DTouchView:collectionView 247 | clickTarget:nil 248 | clickAction:nil 249 | argument:nil 250 | forNavigation:navigation]; 251 | } 252 | 253 | - (void)yj_active3DTouchView:(UIView *)view 254 | clickTarget:(NSObject *)target 255 | clickAction:(SEL)action 256 | argument:(id)argument 257 | forNavigation:(UINavigationController *)navigation 258 | { 259 | if ([self yj3d_private_support3DTouch] == NO || view == nil || navigation == nil) { 260 | return; 261 | } 262 | 263 | if (target && action && [target respondsToSelector:action] == NO) { 264 | return; 265 | } 266 | 267 | YJ3DTouchConfig *config = [self yj3d_private_3DTouchConfigForPreviewSourceView:view extractDetailVC:NO]; 268 | if (config == nil) { 269 | config = [YJ3DTouchConfig new]; 270 | } 271 | 272 | config.navigation = navigation; 273 | config.clickActionTarget = target; 274 | config.clickAction = action; 275 | config.argument = argument; 276 | 277 | [self yj_active3DTouchView:view touchConfig:config]; 278 | } 279 | 280 | - (void)yj_active3DTouchView:(UIView *)view touchConfig:(YJ3DTouchConfig *)touchConfig { 281 | if ([self yj3d_private_support3DTouch] == NO) { 282 | return; 283 | } 284 | 285 | if ([self yj3d_private_checkValidWithView:view touchConfig:touchConfig] == NO) { 286 | return; 287 | } 288 | 289 | [view yj3d_private_set3DTouchConfig:touchConfig]; 290 | 291 | if ([view yj3d_private_hasRegistered3DTouch]) { 292 | return; 293 | } 294 | 295 | [view yj3d_private_setPreviewDelegateController:self]; 296 | 297 | if (touchConfig.navigation) { 298 | [YJ3DTouchUtil safeSwizzleOriginMethod:@selector(pushViewController:animated:) 299 | withTargetMethod:@selector(yj3d_private_pushViewController:animated:) 300 | forClass:[UINavigationController class]]; 301 | } else if (touchConfig.presentingViewController) { 302 | [YJ3DTouchUtil safeSwizzleOriginMethod:@selector(presentViewController:animated:completion:) 303 | withTargetMethod:@selector(yj3d_private_presentViewController:animated:completion:) 304 | forClass:[UIViewController class]]; 305 | } 306 | 307 | if ([view isKindOfClass:[UITableView class]]) { 308 | [self yj3d_private_active3DTouchForTableView:(UITableView *)view]; 309 | } else if ([view isKindOfClass:[UICollectionView class]]) { 310 | [self yj3d_private_active3DTouchForCollectionView:(UICollectionView *)view]; 311 | } else { 312 | [self registerForPreviewingWithDelegate:self sourceView:view]; 313 | } 314 | 315 | [view yj3d_private_setHasRegistered3DTouch:YES]; 316 | } 317 | 318 | // previewController will call this method, eg: detailVC 319 | - (NSArray> *)previewActionItems { 320 | UIViewController *detailVC = self; 321 | UIViewController *hostVC = [detailVC yj3d_private_hostVC]; 322 | if (hostVC == nil) { 323 | return nil; 324 | } 325 | 326 | UIView *currentPreviewingView = [hostVC yj3d_private_currentPreviewingView]; 327 | YJ3DTouchConfig *config = [hostVC yj3d_private_3DTouchConfigForPreviewSourceView:currentPreviewingView extractDetailVC:NO]; 328 | 329 | return config.previewActionItems; 330 | } 331 | 332 | #pragma mark - UIViewControllerPreviewingDelegate 333 | - (UIViewController *)previewingContext:(id )previewingContext 334 | viewControllerForLocation:(CGPoint)location 335 | { 336 | UIView *sourceView = [previewingContext sourceView]; 337 | [self yj3d_private_setCurrentPreviewingView:sourceView]; 338 | 339 | // trigger will peek 340 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_WillPeek; 341 | if ([self yj3d_private_3DTouchActionCanContinue] == NO) { 342 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_Cancel; 343 | return nil; 344 | } 345 | 346 | kYJ3DTouch_VC_Prepared = YES; 347 | YJ3DTouchConfig *config = [self yj3d_private_3DTouchConfigForPreviewSourceView:sourceView extractDetailVC:YES]; 348 | kYJ3DTouch_VC_Prepared = NO; 349 | 350 | UIViewController *detailVC = [(config.navigation ?: config.presentingViewController) yj3d_private_detailVC]; 351 | 352 | if (detailVC) { 353 | detailVC.yj_3DTouchStatus = YJ3DTouchStatus_Previewing; 354 | [detailVC yj3d_private_setHostVC:self]; 355 | } else { 356 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_Cancel; 357 | } 358 | 359 | if (config.navigation) { 360 | [config.navigation yj3d_private_setActivePush3DTouch:NO]; 361 | [config.navigation yj3d_private_setDetailVC:nil]; 362 | } else { 363 | [config.presentingViewController yj3d_private_setActivePresent3DTouch:NO]; 364 | [config.presentingViewController yj3d_private_setDetailVC:nil]; 365 | } 366 | 367 | return detailVC; 368 | } 369 | 370 | - (void)previewingContext:(id )previewingContext 371 | commitViewController:(UIViewController *)viewControllerToCommit 372 | { 373 | [self yj3d_private_cancelDelaySetCancelActionStatus]; 374 | 375 | viewControllerToCommit.yj_3DTouchStatus = YJ3DTouchStatus_None; 376 | 377 | UIView *sourceView = [previewingContext sourceView]; 378 | 379 | // trigger will pop 380 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_WillPop; 381 | if ([self yj3d_private_3DTouchActionCanContinue] == NO) { 382 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_Cancel; 383 | return; 384 | } 385 | 386 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_DidPop; 387 | viewControllerToCommit.yj_3DTouchStatus = YJ3DTouchStatus_Poped; 388 | 389 | YJ3DTouchConfig *config = [self yj3d_private_3DTouchConfigForPreviewSourceView:sourceView extractDetailVC:NO]; 390 | 391 | if (config.navigation) { 392 | [config.navigation pushViewController:viewControllerToCommit animated:YES]; 393 | } else { 394 | [config.presentingViewController presentViewController:viewControllerToCommit animated:YES completion:nil]; 395 | } 396 | } 397 | 398 | #pragma mark - Private 399 | - (BOOL)yj3d_private_checkValidWithView:(UIView *)view touchConfig:(YJ3DTouchConfig *)touchConfig { 400 | if (view == nil) { 401 | return NO; 402 | } 403 | 404 | if (touchConfig.navigation == nil && touchConfig.presentingViewController == nil) { 405 | return NO; 406 | } 407 | 408 | if ([view isKindOfClass:[UITableView class]]) { 409 | UITableView *tableView = (UITableView *)view; 410 | BOOL valid = [tableView.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]; 411 | return valid; 412 | } 413 | 414 | if ([view isKindOfClass:[UICollectionView class]]) { 415 | UICollectionView *collectionView = (UICollectionView *)view; 416 | BOOL valid = [collectionView.delegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]; 417 | return valid; 418 | } 419 | 420 | BOOL valid = (touchConfig.clickActionTarget && touchConfig.clickAction && 421 | [touchConfig.clickActionTarget respondsToSelector:touchConfig.clickAction]); 422 | return valid; 423 | } 424 | 425 | - (BOOL)yj3d_private_support3DTouch { 426 | UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; 427 | if ([keyWindow respondsToSelector:@selector(traitCollection)] 428 | && [keyWindow.traitCollection respondsToSelector:@selector(forceTouchCapability)]) 429 | { 430 | return keyWindow.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable; 431 | } 432 | 433 | return NO; 434 | } 435 | 436 | - (BOOL)yj3d_private_3DTouchActionCanContinue { 437 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 438 | } 439 | 440 | - (void)yj3d_private_update3DTouchActionCanContinue:(BOOL)canContinue { 441 | objc_setAssociatedObject(self, @selector(yj3d_private_3DTouchActionCanContinue), @(canContinue), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 442 | } 443 | 444 | - (UIView *)yj3d_private_currentPreviewingView { 445 | return [YJ3DTouchUtil associatedObjectWithkey:_cmd forTarget:self]; 446 | } 447 | 448 | - (void)yj3d_private_setCurrentPreviewingView:(UIView *)view { 449 | [YJ3DTouchUtil setAssociatedObject:view withKey:@selector(yj3d_private_currentPreviewingView) policy:OBJC_ASSOCIATION_ASSIGN forTarget:self]; 450 | } 451 | 452 | - (YJ3DTouchConfig *)yj3d_private_3DTouchConfigForPreviewSourceView:(UIView *)sourceView 453 | extractDetailVC:(BOOL)extract { 454 | YJ3DTouchConfig *config = [sourceView yj3d_private_3DTouchConfig]; 455 | 456 | UIView *listView = nil; 457 | UIView *listCell = nil; 458 | 459 | if ([sourceView isKindOfClass:[UITableViewCell class]]) { 460 | UITableViewCell *cell = (UITableViewCell *)sourceView; 461 | UITableView *tableView = [cell yj3d_private_cell_tableView]; 462 | if (tableView) { 463 | config = [tableView yj3d_private_3DTouchConfig]; 464 | 465 | listView = tableView; 466 | listCell = cell; 467 | } 468 | } else if ([sourceView isKindOfClass:[UICollectionViewCell class]]) { 469 | UICollectionViewCell *cell = (UICollectionViewCell *)sourceView; 470 | UICollectionView *collectionView = [cell yj3d_private_cell_CollectionView]; 471 | if (collectionView) { 472 | config = [collectionView yj3d_private_3DTouchConfig]; 473 | 474 | listView = collectionView; 475 | listCell = cell; 476 | 477 | BOOL notSupportSelect = [collectionView.delegate respondsToSelector:@selector(collectionView:shouldSelectItemAtIndexPath:)] && ([collectionView.delegate collectionView:collectionView shouldSelectItemAtIndexPath:[collectionView indexPathForCell:cell]] == NO); 478 | if ([collectionView.delegate respondsToSelector:@selector(collectionView:shouldSelectItemAtIndexPath:)] && notSupportSelect) { 479 | config = nil; 480 | listView = nil; 481 | listCell = nil; 482 | extract = NO; 483 | } 484 | } 485 | } 486 | 487 | if (extract) { 488 | if (config.navigation) { 489 | [config.navigation yj3d_private_setActivePush3DTouch:YES]; 490 | } else { 491 | [config.presentingViewController yj3d_private_setActivePresent3DTouch:YES]; 492 | } 493 | 494 | if ([listView isKindOfClass:[UITableView class]]) { 495 | UITableView *tableView = (UITableView *)listView; 496 | [tableView.delegate tableView:tableView 497 | didSelectRowAtIndexPath:[tableView indexPathForCell:(UITableViewCell *)listCell]]; 498 | } else if ([listView isKindOfClass:[UICollectionView class]]) { 499 | UICollectionView *collectionView = (UICollectionView *)listView; 500 | 501 | BOOL notSupportSelect = [collectionView.delegate respondsToSelector:@selector(collectionView:shouldSelectItemAtIndexPath:)] && ([collectionView.delegate collectionView:collectionView shouldSelectItemAtIndexPath:[collectionView indexPathForCell:(UICollectionViewCell *)listCell]] == NO); 502 | BOOL canResponds = [collectionView.delegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]; 503 | 504 | if (!notSupportSelect && canResponds) { 505 | [collectionView.delegate collectionView:collectionView didSelectItemAtIndexPath:[collectionView indexPathForCell:(UICollectionViewCell *)listCell]]; 506 | } 507 | } else { 508 | #pragma clang diagnostic push 509 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 510 | [config.clickActionTarget performSelector:config.clickAction withObject:config.argument]; 511 | #pragma clang diagnostic pop 512 | } 513 | } 514 | 515 | return config; 516 | } 517 | 518 | - (void)yj3d_private_viewDidLoad { 519 | if (kYJ3DTouch_VC_Prepared) { 520 | self.yj_3DTouchStatus = YJ3DTouchStatus_Prepared; 521 | } 522 | 523 | [self yj3d_private_viewDidLoad]; 524 | } 525 | 526 | - (void)yj3d_private_viewWillAppear:(BOOL)animated { 527 | [self yj3d_private_viewWillAppear:animated]; 528 | self.yj_appearStatus = YJ3DViewControllerAppear_WillAppear; 529 | } 530 | 531 | - (void)yj3d_private_viewDidAppear:(BOOL)animated { 532 | [self yj3d_private_viewDidAppear:animated]; 533 | self.yj_appearStatus = YJ3DViewControllerAppear_DidAppear; 534 | 535 | UIViewController *hostVC = [self yj3d_private_hostVC]; 536 | if (hostVC == nil) { 537 | return; 538 | } 539 | 540 | if (self.yj_3DTouchStatus == YJ3DTouchStatus_Previewing) { 541 | hostVC.yj_3DTouchActionStatus = YJ3DTouchActionStatus_DidPeek; 542 | } 543 | } 544 | 545 | - (void)yj3d_private_viewWillDisappear:(BOOL)animated { 546 | [self yj3d_private_viewWillDisappear:animated]; 547 | self.yj_appearStatus = YJ3DViewControllerAppear_WillDisappear; 548 | } 549 | 550 | - (void)yj3d_private_viewDidDisappear:(BOOL)animated { 551 | [self yj3d_private_viewDidDisappear:animated]; 552 | self.yj_appearStatus = YJ3DViewControllerAppear_DidDisappear; 553 | 554 | UIViewController *hostVC = [self yj3d_private_hostVC]; 555 | if (hostVC == nil) { 556 | return; 557 | } 558 | 559 | if (self.yj_3DTouchStatus == YJ3DTouchStatus_Previewing) { 560 | [hostVC performSelector:@selector(yj3d_private_delaySetCancelActionStatus) withObject:nil afterDelay:0.2]; 561 | } 562 | 563 | self.yj_3DTouchStatus = YJ3DTouchStatus_None; 564 | } 565 | 566 | - (void)yj3d_private_delaySetCancelActionStatus { 567 | self.yj_3DTouchActionStatus = YJ3DTouchActionStatus_Cancel; 568 | } 569 | 570 | - (void)yj3d_private_cancelDelaySetCancelActionStatus { 571 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(yj3d_private_delaySetCancelActionStatus) object:nil]; 572 | } 573 | 574 | - (void)yj3d_private_active3DTouchForTableView:(UITableView *)tableView { 575 | [YJ3DTouchUtil hookTableViewDataSource:tableView.dataSource withBlock:^(UITableViewCell *cell, UITableView *tableView) { 576 | if ([cell yj3d_private_hasRegistered3DTouch]) { 577 | return; 578 | } 579 | 580 | UIViewController *vc = [tableView yj3d_private_previewDelegateController]; 581 | if (vc) { 582 | [cell yj3d_private_setHasRegistered3DTouch:YES]; 583 | [cell yj3d_private_cell_setTableView:tableView]; 584 | [vc registerForPreviewingWithDelegate:vc sourceView:cell]; 585 | } 586 | }]; 587 | 588 | for (UITableViewCell *cell in tableView.visibleCells) { 589 | if ([cell yj3d_private_hasRegistered3DTouch]) { 590 | continue; 591 | } 592 | 593 | [cell yj3d_private_setHasRegistered3DTouch:YES]; 594 | UIViewController *vc = [tableView yj3d_private_previewDelegateController]; 595 | [vc registerForPreviewingWithDelegate:vc sourceView:cell]; 596 | [cell yj3d_private_cell_setTableView:tableView]; 597 | } 598 | } 599 | 600 | - (void)yj3d_private_active3DTouchForCollectionView:(UICollectionView *)collectionView { 601 | [YJ3DTouchUtil hookCollectionViewDataSource:collectionView.dataSource withBlock:^(UICollectionViewCell *cell, UICollectionView *collectionView) { 602 | if ([cell yj3d_private_hasRegistered3DTouch]) { 603 | return; 604 | } 605 | 606 | UIViewController *vc = [collectionView yj3d_private_previewDelegateController]; 607 | if (vc) { 608 | [vc registerForPreviewingWithDelegate:vc sourceView:cell]; 609 | [cell yj3d_private_setHasRegistered3DTouch:YES]; 610 | [cell yj3d_private_cell_setCollectionView:collectionView]; 611 | } 612 | }]; 613 | 614 | for (UICollectionViewCell *cell in collectionView.visibleCells) { 615 | if ([cell yj3d_private_hasRegistered3DTouch]) { 616 | continue; 617 | } 618 | 619 | [cell yj3d_private_setHasRegistered3DTouch:YES]; 620 | UIViewController *vc = [collectionView yj3d_private_previewDelegateController]; 621 | [vc registerForPreviewingWithDelegate:vc sourceView:cell]; 622 | [cell yj3d_private_cell_setCollectionView:collectionView]; 623 | } 624 | } 625 | 626 | @end 627 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/YJ3DTouchConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // YJ3DTouchConfig.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/31. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface YJ3DTouchConfig : NSObject 13 | 14 | /// Priority in using the navigation 15 | @property(nonatomic, weak) UINavigationController *navigation; 16 | @property(nonatomic, weak) UIViewController *presentingViewController; 17 | 18 | /// In addition to the UITableView, need to specify the responder 19 | @property(nonatomic, weak) NSObject *clickActionTarget; 20 | @property(nonatomic, assign) SEL clickAction; 21 | @property(nonatomic, weak) id argument; 22 | 23 | @property(nonatomic, strong) NSArray > *previewActionItems; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/YJ3DTouchConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // YJ3DTouchConfig.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/31. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "YJ3DTouchConfig.h" 10 | 11 | @implementation YJ3DTouchConfig 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/YJ3DTouchUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // YJ3DTouchUtil.h 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/4/1. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface YJ3DTouchUtil : NSObject 14 | 15 | + (void)safeSwizzleOriginMethod:(SEL)origSel withTargetMethod:(SEL)altSel forClass:(Class)cls; 16 | 17 | + (id)associatedObjectWithkey:(const void *)key forTarget:(id)target; 18 | + (void)setAssociatedObject:(id)object withKey:(const void *)key policy:(objc_AssociationPolicy)policy forTarget:(id)target; 19 | 20 | + (void)hookTableViewDataSource:(id)dataSource withBlock:(void(^)(UITableViewCell *cell, UITableView *tableView))block; 21 | + (void)hookCollectionViewDataSource:(id)dataSource withBlock:(void(^)(UICollectionViewCell *cell, UICollectionView *collectionView))block; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /YJ3DTouch/YJ3DTouch/YJ3DTouchUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // YJ3DTouchUtil.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/4/1. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import "YJ3DTouchUtil.h" 10 | 11 | #pragma mark - YJ3DWeakAssociateContainer 12 | @interface YJ3DWeakAssociateContainer : NSObject 13 | @property (nonatomic, weak) id obj; 14 | @end 15 | 16 | @implementation YJ3DWeakAssociateContainer 17 | @end 18 | 19 | #pragma mark - YJ3DTouchUtil 20 | @implementation YJ3DTouchUtil 21 | 22 | + (void)hookTableViewDataSource:(id)dataSource withBlock:(void(^)(UITableViewCell *cell, UITableView *tableView))block { 23 | Class targetClass = [dataSource class]; 24 | SEL originSEL = @selector(tableView:cellForRowAtIndexPath:); 25 | if ([self hasSwizzledForTarget:targetClass swizzleSelector:originSEL]) { 26 | return; 27 | } 28 | [self setHasSwizzled:YES forTarget:targetClass swizzleSelector:originSEL]; 29 | 30 | Method originMethod = class_getInstanceMethod(targetClass, originSEL); 31 | UITableViewCell *(*originIMP)(__unsafe_unretained id, SEL, __strong id, __strong id) = NULL; 32 | originIMP = (__typeof__(originIMP))method_getImplementation(originMethod); 33 | 34 | __block id targetBlock = ^UITableViewCell *(__unsafe_unretained id obj,UITableView *tableView, NSIndexPath *indexPath){ 35 | UITableViewCell *cell = originIMP(obj, originSEL, tableView, indexPath); 36 | block(cell, tableView); 37 | return cell; 38 | }; 39 | 40 | IMP targetIMP = imp_implementationWithBlock(targetBlock); 41 | method_setImplementation(originMethod, targetIMP); 42 | } 43 | 44 | + (void)hookCollectionViewDataSource:(id)dataSource withBlock:(void(^)(UICollectionViewCell *cell, UICollectionView *collectionView))block { 45 | Class targetClass = [dataSource class]; 46 | SEL originSEL = @selector(collectionView:cellForItemAtIndexPath:); 47 | if ([self hasSwizzledForTarget:targetClass swizzleSelector:originSEL]) { 48 | return; 49 | } 50 | [self setHasSwizzled:YES forTarget:targetClass swizzleSelector:originSEL]; 51 | 52 | Method originMethod = class_getInstanceMethod(targetClass, originSEL); 53 | UICollectionViewCell *(*originIMP)(__unsafe_unretained id, SEL, __strong id, __strong id) = NULL; 54 | originIMP = (__typeof__(originIMP))method_getImplementation(originMethod); 55 | 56 | __block id targetBlock = ^UICollectionViewCell *(__unsafe_unretained id obj,UICollectionView *collectionView, NSIndexPath *indexPath){ 57 | UICollectionViewCell *cell = originIMP(obj, originSEL, collectionView, indexPath); 58 | block(cell, collectionView); 59 | return cell; 60 | }; 61 | 62 | IMP targetIMP = imp_implementationWithBlock(targetBlock); 63 | method_setImplementation(originMethod, targetIMP); 64 | } 65 | 66 | + (void)safeSwizzleOriginMethod:(SEL)origSel withTargetMethod:(SEL)altSel forClass:(Class)cls { 67 | if ([self hasSwizzledForTarget:cls swizzleSelector:origSel]) { 68 | return; 69 | } 70 | [self setHasSwizzled:YES forTarget:cls swizzleSelector:origSel]; 71 | 72 | method_exchangeImplementations(class_getInstanceMethod(cls, origSel), 73 | class_getInstanceMethod(cls, altSel)); 74 | } 75 | 76 | + (id)associatedObjectWithkey:(const void *)key forTarget:(id)target { 77 | if (target == nil) { 78 | return nil; 79 | } 80 | 81 | id object = objc_getAssociatedObject(target, key); 82 | if ([object isKindOfClass:[YJ3DWeakAssociateContainer class]]) { 83 | return [(YJ3DWeakAssociateContainer *)object obj]; 84 | } 85 | 86 | return object; 87 | } 88 | 89 | + (void)setAssociatedObject:(id)object withKey:(const void *)key policy:(objc_AssociationPolicy)policy forTarget:(id)target { 90 | if (target == nil) { 91 | return; 92 | } 93 | 94 | if (policy == OBJC_ASSOCIATION_ASSIGN) { 95 | YJ3DWeakAssociateContainer *container = nil; 96 | if (object) { 97 | container = objc_getAssociatedObject(target, key); 98 | if (container == nil || [container isKindOfClass:[YJ3DWeakAssociateContainer class]] == NO) { 99 | container = [YJ3DWeakAssociateContainer new]; 100 | } 101 | container.obj = object; 102 | } 103 | objc_setAssociatedObject(target, key, container, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 104 | } else { 105 | objc_setAssociatedObject(target, key, object, policy); 106 | } 107 | } 108 | 109 | #pragma mark - Private 110 | + (BOOL)hasSwizzledForTarget:(id)object swizzleSelector:(SEL)selector { 111 | return [objc_getAssociatedObject(object, selector) boolValue]; 112 | } 113 | 114 | + (void)setHasSwizzled:(BOOL)swizzled forTarget:(id)object swizzleSelector:(SEL)selector { 115 | objc_setAssociatedObject(object, 116 | selector, 117 | @(swizzled), 118 | OBJC_ASSOCIATION_RETAIN_NONATOMIC); 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /YJ3DTouch/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YJ3DTouch 4 | // 5 | // Created by hyman on 2017/3/30. 6 | // Copyright © 2017年 hyman. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hyman00/YJ3DTouch/9e9aefedbb8974e9803d02d85d700278d38b2d2e/tip.gif --------------------------------------------------------------------------------