├── .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
--------------------------------------------------------------------------------