├── .gitignore
├── LICENSE
├── README.md
├── WKWebViewTest.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
└── WKWebViewTest
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── Base.lproj
└── MainMenu.xib
├── Info.plist
├── WKWebView+Screenshot.h
├── WKWebView+Screenshot.m
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Lemon Mojo - Felix Deimel e.U.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of WKWebView+Screenshot nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WKWebView+Screenshot
2 |
3 | **Update October 2, 2017**: These workarounds are **not required** anymore when targeting **10.13+** because Apple added an official API. (see [takeSnapshotWithConfiguration:completionHandler:](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshotwithconfiguration))
4 |
5 | **Update January 18, 2017**: Found another workaround by faking the presence of the WKView.
6 |
7 | **Update October 22, 2016**: This doesn't work anymore in 10.12.1 (maybe even 10.12, didn't test) because `WKView` is not exposed as a subview of `WKWebView` anymore. There is however a new API in the making that will allow taking screenshots of `WKWebView`s without using private API: https://bugs.webkit.org/show_bug.cgi?id=161450
8 |
9 | Summary
10 | -------
11 |
12 | WKWebView+Screenshot provides simple methods that use private(!) APIs of the WebKit framework to capture a screenshot of a WKWebView running on OS X.
13 |
14 | Let me repeat the important part: This way of capturing screenshots of a WKWebView may break at any time since it's using private and undocumented APIs! It's also very likely that Apple would not allow an App that uses this method in the Mac App Store!
15 |
16 | Unfortunately, I'm not aware of any other way to capture screenshots of an WKWebView at the moment so this is probably your best bet if you're publishing outside of the App Store.
17 |
18 | ARC is currently not supported but it should be easy to convert the project. Alternatively, if your project is using ARC, you can also just disable it for WKWebView+Screenshot.m ([see this StackOverflow post](http://stackoverflow.com/questions/6646052)).
19 |
20 | Sample Usage
21 | ------------
22 |
23 | Asynchronous method:
24 | ```objc
25 | [wkWebView captureScreenshotWithCompletionHandler:^(NSImage *screenshot) {
26 | self.imageView.image = screenshot;
27 | }];
28 | ```
29 |
30 | Synchronous method:
31 | ```objc
32 | dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
33 | self.imageView.image = [wkWebView captureScreenshotWithTimeout:timeout];
34 | ```
35 |
36 | License
37 | -------
38 |
39 | [New BSD License](http://en.wikipedia.org/wiki/BSD_licenses). For the full license text, see [here](https://raw.github.com/LemonMojo/WKWebView-Screenshot/master/LICENSE).
40 |
41 | Credits
42 | -------
43 | WKWebView+Screenshot was created by [Felix Deimel](https://github.com/LemonMojo).
44 |
--------------------------------------------------------------------------------
/WKWebViewTest.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E7171BC41B281A43002DE8A7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E7171BC31B281A43002DE8A7 /* AppDelegate.m */; };
11 | E7171BC71B281A43002DE8A7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E7171BC61B281A43002DE8A7 /* main.m */; };
12 | E7171BC91B281A43002DE8A7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7171BC81B281A43002DE8A7 /* Assets.xcassets */; };
13 | E7171BCC1B281A43002DE8A7 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7171BCA1B281A43002DE8A7 /* MainMenu.xib */; };
14 | E7171BD41B281A67002DE8A7 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7171BD31B281A67002DE8A7 /* WebKit.framework */; };
15 | E7171BD61B2821F0002DE8A7 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7171BD51B2821F0002DE8A7 /* Cocoa.framework */; };
16 | E7171BD81B2821F4002DE8A7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7171BD71B2821F4002DE8A7 /* Foundation.framework */; };
17 | E76EA6601B876EB300AAF8E4 /* WKWebView+Screenshot.m in Sources */ = {isa = PBXBuildFile; fileRef = E76EA65F1B876EB300AAF8E4 /* WKWebView+Screenshot.m */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | E7171BBF1B281A43002DE8A7 /* WKWebViewTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WKWebViewTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
22 | E7171BC21B281A43002DE8A7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
23 | E7171BC31B281A43002DE8A7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
24 | E7171BC61B281A43002DE8A7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
25 | E7171BC81B281A43002DE8A7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | E7171BCB1B281A43002DE8A7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
27 | E7171BCD1B281A43002DE8A7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | E7171BD31B281A67002DE8A7 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
29 | E7171BD51B2821F0002DE8A7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
30 | E7171BD71B2821F4002DE8A7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
31 | E76EA6561B875F0B00AAF8E4 /* WebCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebCore.framework; path = System/Library/Frameworks/WebKit.framework/Frameworks/WebCore.framework; sourceTree = SDKROOT; };
32 | E76EA65E1B876EB300AAF8E4 /* WKWebView+Screenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WKWebView+Screenshot.h"; sourceTree = ""; };
33 | E76EA65F1B876EB300AAF8E4 /* WKWebView+Screenshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+Screenshot.m"; sourceTree = ""; };
34 | /* End PBXFileReference section */
35 |
36 | /* Begin PBXFrameworksBuildPhase section */
37 | E7171BBC1B281A43002DE8A7 /* Frameworks */ = {
38 | isa = PBXFrameworksBuildPhase;
39 | buildActionMask = 2147483647;
40 | files = (
41 | E7171BD81B2821F4002DE8A7 /* Foundation.framework in Frameworks */,
42 | E7171BD61B2821F0002DE8A7 /* Cocoa.framework in Frameworks */,
43 | E7171BD41B281A67002DE8A7 /* WebKit.framework in Frameworks */,
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | E7171BB61B281A43002DE8A7 = {
51 | isa = PBXGroup;
52 | children = (
53 | E7171BC11B281A43002DE8A7 /* WKWebViewTest */,
54 | E76EA65B1B87637F00AAF8E4 /* Frameworks */,
55 | E7171BC01B281A43002DE8A7 /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | E7171BC01B281A43002DE8A7 /* Products */ = {
60 | isa = PBXGroup;
61 | children = (
62 | E7171BBF1B281A43002DE8A7 /* WKWebViewTest.app */,
63 | );
64 | name = Products;
65 | sourceTree = "";
66 | };
67 | E7171BC11B281A43002DE8A7 /* WKWebViewTest */ = {
68 | isa = PBXGroup;
69 | children = (
70 | E7171BC21B281A43002DE8A7 /* AppDelegate.h */,
71 | E7171BC31B281A43002DE8A7 /* AppDelegate.m */,
72 | E76EA65E1B876EB300AAF8E4 /* WKWebView+Screenshot.h */,
73 | E76EA65F1B876EB300AAF8E4 /* WKWebView+Screenshot.m */,
74 | E7171BC81B281A43002DE8A7 /* Assets.xcassets */,
75 | E7171BCA1B281A43002DE8A7 /* MainMenu.xib */,
76 | E7171BCD1B281A43002DE8A7 /* Info.plist */,
77 | E7171BC51B281A43002DE8A7 /* Supporting Files */,
78 | );
79 | path = WKWebViewTest;
80 | sourceTree = "";
81 | };
82 | E7171BC51B281A43002DE8A7 /* Supporting Files */ = {
83 | isa = PBXGroup;
84 | children = (
85 | E7171BC61B281A43002DE8A7 /* main.m */,
86 | );
87 | name = "Supporting Files";
88 | sourceTree = "";
89 | };
90 | E76EA65B1B87637F00AAF8E4 /* Frameworks */ = {
91 | isa = PBXGroup;
92 | children = (
93 | E76EA6561B875F0B00AAF8E4 /* WebCore.framework */,
94 | E7171BD71B2821F4002DE8A7 /* Foundation.framework */,
95 | E7171BD51B2821F0002DE8A7 /* Cocoa.framework */,
96 | E7171BD31B281A67002DE8A7 /* WebKit.framework */,
97 | );
98 | name = Frameworks;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | E7171BBE1B281A43002DE8A7 /* WKWebViewTest */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = E7171BD01B281A43002DE8A7 /* Build configuration list for PBXNativeTarget "WKWebViewTest" */;
107 | buildPhases = (
108 | E7171BBB1B281A43002DE8A7 /* Sources */,
109 | E7171BBC1B281A43002DE8A7 /* Frameworks */,
110 | E7171BBD1B281A43002DE8A7 /* Resources */,
111 | );
112 | buildRules = (
113 | );
114 | dependencies = (
115 | );
116 | name = WKWebViewTest;
117 | productName = WKWebViewTest;
118 | productReference = E7171BBF1B281A43002DE8A7 /* WKWebViewTest.app */;
119 | productType = "com.apple.product-type.application";
120 | };
121 | /* End PBXNativeTarget section */
122 |
123 | /* Begin PBXProject section */
124 | E7171BB71B281A43002DE8A7 /* Project object */ = {
125 | isa = PBXProject;
126 | attributes = {
127 | LastUpgradeCheck = 0700;
128 | ORGANIZATIONNAME = "Lemon Mojo";
129 | TargetAttributes = {
130 | E7171BBE1B281A43002DE8A7 = {
131 | CreatedOnToolsVersion = 7.0;
132 | };
133 | };
134 | };
135 | buildConfigurationList = E7171BBA1B281A43002DE8A7 /* Build configuration list for PBXProject "WKWebViewTest" */;
136 | compatibilityVersion = "Xcode 3.2";
137 | developmentRegion = English;
138 | hasScannedForEncodings = 0;
139 | knownRegions = (
140 | en,
141 | Base,
142 | );
143 | mainGroup = E7171BB61B281A43002DE8A7;
144 | productRefGroup = E7171BC01B281A43002DE8A7 /* Products */;
145 | projectDirPath = "";
146 | projectRoot = "";
147 | targets = (
148 | E7171BBE1B281A43002DE8A7 /* WKWebViewTest */,
149 | );
150 | };
151 | /* End PBXProject section */
152 |
153 | /* Begin PBXResourcesBuildPhase section */
154 | E7171BBD1B281A43002DE8A7 /* Resources */ = {
155 | isa = PBXResourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | E7171BC91B281A43002DE8A7 /* Assets.xcassets in Resources */,
159 | E7171BCC1B281A43002DE8A7 /* MainMenu.xib in Resources */,
160 | );
161 | runOnlyForDeploymentPostprocessing = 0;
162 | };
163 | /* End PBXResourcesBuildPhase section */
164 |
165 | /* Begin PBXSourcesBuildPhase section */
166 | E7171BBB1B281A43002DE8A7 /* Sources */ = {
167 | isa = PBXSourcesBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | E76EA6601B876EB300AAF8E4 /* WKWebView+Screenshot.m in Sources */,
171 | E7171BC71B281A43002DE8A7 /* main.m in Sources */,
172 | E7171BC41B281A43002DE8A7 /* AppDelegate.m in Sources */,
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | };
176 | /* End PBXSourcesBuildPhase section */
177 |
178 | /* Begin PBXVariantGroup section */
179 | E7171BCA1B281A43002DE8A7 /* MainMenu.xib */ = {
180 | isa = PBXVariantGroup;
181 | children = (
182 | E7171BCB1B281A43002DE8A7 /* Base */,
183 | );
184 | name = MainMenu.xib;
185 | sourceTree = "";
186 | };
187 | /* End PBXVariantGroup section */
188 |
189 | /* Begin XCBuildConfiguration section */
190 | E7171BCE1B281A43002DE8A7 /* Debug */ = {
191 | isa = XCBuildConfiguration;
192 | buildSettings = {
193 | ALWAYS_SEARCH_USER_PATHS = NO;
194 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
195 | CLANG_CXX_LIBRARY = "libc++";
196 | CLANG_ENABLE_MODULES = YES;
197 | CLANG_ENABLE_OBJC_ARC = YES;
198 | CLANG_WARN_BOOL_CONVERSION = YES;
199 | CLANG_WARN_CONSTANT_CONVERSION = YES;
200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
201 | CLANG_WARN_EMPTY_BODY = YES;
202 | CLANG_WARN_ENUM_CONVERSION = YES;
203 | CLANG_WARN_INT_CONVERSION = YES;
204 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
205 | CLANG_WARN_UNREACHABLE_CODE = YES;
206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
207 | CODE_SIGN_IDENTITY = "-";
208 | COPY_PHASE_STRIP = NO;
209 | DEBUG_INFORMATION_FORMAT = dwarf;
210 | ENABLE_STRICT_OBJC_MSGSEND = YES;
211 | ENABLE_TESTABILITY = YES;
212 | GCC_C_LANGUAGE_STANDARD = gnu99;
213 | GCC_DYNAMIC_NO_PIC = NO;
214 | GCC_NO_COMMON_BLOCKS = YES;
215 | GCC_OPTIMIZATION_LEVEL = 0;
216 | GCC_PREPROCESSOR_DEFINITIONS = (
217 | "DEBUG=1",
218 | "$(inherited)",
219 | );
220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
221 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
222 | GCC_WARN_UNDECLARED_SELECTOR = YES;
223 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
224 | GCC_WARN_UNUSED_FUNCTION = YES;
225 | GCC_WARN_UNUSED_VARIABLE = YES;
226 | MACOSX_DEPLOYMENT_TARGET = 10.10;
227 | MTL_ENABLE_DEBUG_INFO = YES;
228 | ONLY_ACTIVE_ARCH = YES;
229 | SDKROOT = macosx;
230 | };
231 | name = Debug;
232 | };
233 | E7171BCF1B281A43002DE8A7 /* Release */ = {
234 | isa = XCBuildConfiguration;
235 | buildSettings = {
236 | ALWAYS_SEARCH_USER_PATHS = NO;
237 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
238 | CLANG_CXX_LIBRARY = "libc++";
239 | CLANG_ENABLE_MODULES = YES;
240 | CLANG_ENABLE_OBJC_ARC = YES;
241 | CLANG_WARN_BOOL_CONVERSION = YES;
242 | CLANG_WARN_CONSTANT_CONVERSION = YES;
243 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
244 | CLANG_WARN_EMPTY_BODY = YES;
245 | CLANG_WARN_ENUM_CONVERSION = YES;
246 | CLANG_WARN_INT_CONVERSION = YES;
247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
248 | CLANG_WARN_UNREACHABLE_CODE = YES;
249 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
250 | CODE_SIGN_IDENTITY = "-";
251 | COPY_PHASE_STRIP = NO;
252 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
253 | ENABLE_NS_ASSERTIONS = NO;
254 | ENABLE_STRICT_OBJC_MSGSEND = YES;
255 | GCC_C_LANGUAGE_STANDARD = gnu99;
256 | GCC_NO_COMMON_BLOCKS = YES;
257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
259 | GCC_WARN_UNDECLARED_SELECTOR = YES;
260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
261 | GCC_WARN_UNUSED_FUNCTION = YES;
262 | GCC_WARN_UNUSED_VARIABLE = YES;
263 | MACOSX_DEPLOYMENT_TARGET = 10.10;
264 | MTL_ENABLE_DEBUG_INFO = NO;
265 | SDKROOT = macosx;
266 | };
267 | name = Release;
268 | };
269 | E7171BD11B281A43002DE8A7 /* Debug */ = {
270 | isa = XCBuildConfiguration;
271 | buildSettings = {
272 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
273 | CLANG_ENABLE_OBJC_ARC = NO;
274 | CLANG_MODULES_AUTOLINK = NO;
275 | COMBINE_HIDPI_IMAGES = YES;
276 | FRAMEWORK_SEARCH_PATHS = (
277 | "$(inherited)",
278 | "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/WebKit.framework/Frameworks",
279 | );
280 | INFOPLIST_FILE = WKWebViewTest/Info.plist;
281 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
282 | OTHER_CPLUSPLUSFLAGS = (
283 | "$(OTHER_CFLAGS)",
284 | "-stdlib=libc++",
285 | );
286 | PRODUCT_BUNDLE_IDENTIFIER = com.lemonmojo.WKWebViewTest;
287 | PRODUCT_NAME = "$(TARGET_NAME)";
288 | };
289 | name = Debug;
290 | };
291 | E7171BD21B281A43002DE8A7 /* Release */ = {
292 | isa = XCBuildConfiguration;
293 | buildSettings = {
294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
295 | CLANG_ENABLE_OBJC_ARC = NO;
296 | CLANG_MODULES_AUTOLINK = NO;
297 | COMBINE_HIDPI_IMAGES = YES;
298 | FRAMEWORK_SEARCH_PATHS = (
299 | "$(inherited)",
300 | "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/WebKit.framework/Frameworks",
301 | );
302 | INFOPLIST_FILE = WKWebViewTest/Info.plist;
303 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
304 | OTHER_CPLUSPLUSFLAGS = (
305 | "$(OTHER_CFLAGS)",
306 | "-stdlib=libc++",
307 | );
308 | PRODUCT_BUNDLE_IDENTIFIER = com.lemonmojo.WKWebViewTest;
309 | PRODUCT_NAME = "$(TARGET_NAME)";
310 | };
311 | name = Release;
312 | };
313 | /* End XCBuildConfiguration section */
314 |
315 | /* Begin XCConfigurationList section */
316 | E7171BBA1B281A43002DE8A7 /* Build configuration list for PBXProject "WKWebViewTest" */ = {
317 | isa = XCConfigurationList;
318 | buildConfigurations = (
319 | E7171BCE1B281A43002DE8A7 /* Debug */,
320 | E7171BCF1B281A43002DE8A7 /* Release */,
321 | );
322 | defaultConfigurationIsVisible = 0;
323 | defaultConfigurationName = Release;
324 | };
325 | E7171BD01B281A43002DE8A7 /* Build configuration list for PBXNativeTarget "WKWebViewTest" */ = {
326 | isa = XCConfigurationList;
327 | buildConfigurations = (
328 | E7171BD11B281A43002DE8A7 /* Debug */,
329 | E7171BD21B281A43002DE8A7 /* Release */,
330 | );
331 | defaultConfigurationIsVisible = 0;
332 | defaultConfigurationName = Release;
333 | };
334 | /* End XCConfigurationList section */
335 | };
336 | rootObject = E7171BB71B281A43002DE8A7 /* Project object */;
337 | }
338 |
--------------------------------------------------------------------------------
/WKWebViewTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WKWebViewTest/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WKWebViewTest
4 | //
5 | // Created by Felix Deimel on 10.06.15.
6 | // Copyright © Lemon Mojo - Felix Deimel e.U. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : NSObject
12 | @end
--------------------------------------------------------------------------------
/WKWebViewTest/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WKWebViewTest
4 | //
5 | // Created by Felix Deimel on 10.06.15.
6 | // Copyright © 2015 Lemon Mojo - Felix Deimel e.U. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import
11 | #import "WKWebView+Screenshot.h"
12 |
13 | @interface AppDelegate ()
14 |
15 | @property (assign) IBOutlet NSWindow *window;
16 | @property (assign) IBOutlet NSTabView *tabView;
17 | @property (assign) IBOutlet NSImageView *imageView;
18 |
19 | @end
20 |
21 | @implementation AppDelegate {
22 | NSMutableArray *m_webViews;
23 | }
24 |
25 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
26 | {
27 | m_webViews = [[NSMutableArray alloc] init];
28 | [self.imageView registeredDraggedTypes];
29 | WKWebViewConfiguration* config = [[[WKWebViewConfiguration alloc] init] autorelease];
30 | WKPreferences* prefs = config.preferences;
31 |
32 | prefs.javaScriptEnabled = YES;
33 | prefs.javaEnabled = YES;
34 | prefs.javaScriptCanOpenWindowsAutomatically = YES;
35 | prefs.plugInsEnabled = YES;
36 |
37 | [m_webViews addObject:[self addWebViewWithConfig:config url:@"http://www.google.com/" toView:[self.tabView.tabViewItems[0] view]]];
38 | [m_webViews addObject:[self addWebViewWithConfig:config url:@"http://www.javatester.org/" toView:[self.tabView.tabViewItems[1] view]]];
39 | [m_webViews addObject:[self addWebViewWithConfig:config url:@"http://www.audiotool.com/app" toView:[self.tabView.tabViewItems[2] view]]];
40 | [m_webViews addObject:[self addWebViewWithConfig:config url:@"http://www.medicalrounds.com/quicktimecheck/troubleshooting.html" toView:[self.tabView.tabViewItems[3] view]]];
41 | }
42 |
43 | - (WKWebView*)addWebViewWithConfig:(WKWebViewConfiguration*)config url:(NSString*)url toView:(NSView*)view
44 | {
45 | WKWebView* v = [[[WKWebView alloc] initWithFrame:view.bounds configuration:config] autorelease];
46 | v.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
47 |
48 | [view addSubview:v];
49 |
50 | NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
51 | [v loadRequest:req];
52 |
53 | return v;
54 | }
55 |
56 | - (IBAction)buttonCaptureScreenshot_action:(id)sender
57 | {
58 | WKWebView* wkWebView = [m_webViews objectAtIndex:[self.tabView indexOfTabViewItem:self.tabView.selectedTabViewItem]];
59 |
60 | [self captureScreenshotAsyncInWKWebView:wkWebView];
61 | }
62 |
63 | - (void)captureScreenshotAsyncInWKWebView:(WKWebView*)wkWebView
64 | {
65 | [wkWebView captureScreenshotWithCompletionHandler:^(NSImage *screenshot) {
66 | self.imageView.image = screenshot;
67 | }];
68 | }
69 |
70 | - (void)captureScreenshotSyncInWKWebView:(WKWebView*)wkWebView
71 | {
72 | dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
73 | self.imageView.image = [wkWebView captureScreenshotWithTimeout:timeout];
74 | }
75 |
76 | @end
--------------------------------------------------------------------------------
/WKWebViewTest/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/WKWebViewTest/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
--------------------------------------------------------------------------------
/WKWebViewTest/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2015 Lemon Mojo - Felix Deimel e.U. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 | NSAppTransportSecurity
34 |
35 | NSAllowsArbitraryLoads
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/WKWebViewTest/WKWebView+Screenshot.h:
--------------------------------------------------------------------------------
1 | //
2 | // LMThumbnailView.h
3 | // WKWebViewTest
4 | //
5 | // Created by Felix Deimel on 21.08.15.
6 | // Copyright (c) 2015 Lemon Mojo - Felix Deimel e.U. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LMFakeWKView : NSObject
12 |
13 | - (instancetype)initWithWebView:(WKWebView*)webView;
14 |
15 | @property (strong) WKWebView* webView;
16 |
17 | @end
18 |
19 | #if WK_API_ENABLED
20 | #if !TARGET_OS_IPHONE
21 | @class WKView;
22 |
23 | NS_CLASS_AVAILABLE(10_10, 8_0) /* WK_CLASS_AVAILABLE(10_10, 8_0) */
24 | @interface _WKThumbnailView : NSView
25 |
26 | - (instancetype)initWithFrame:(NSRect)frame fromWKView:(WKView *)wkView;
27 |
28 | @property (nonatomic) CGFloat scale;
29 | @property (nonatomic, readonly) CGSize snapshotSize;
30 | @property (nonatomic) CGSize maximumSnapshotSize;
31 | @property (nonatomic) BOOL exclusivelyUsesSnapshot;
32 |
33 | // Defaults to NO.
34 | @property (nonatomic) BOOL shouldKeepSnapshotWhenRemovedFromSuperview;
35 |
36 | // This should be removed when all clients go away; it is always YES now.
37 | @property (nonatomic) BOOL usesSnapshot;
38 |
39 | - (void)requestSnapshot;
40 |
41 | - (void)_didTakeSnapshot:(CGImageRef)image;
42 | - (void)_viewWasParented;
43 | - (void)_viewWasUnparented;
44 |
45 | @end
46 | #endif // TARGET_OS_IPHONE
47 | #endif // WK_API_ENABLED
48 |
49 | typedef void (^CaptureScreenshotCompletionHandler) (NSImage* screenshot);
50 |
51 | NS_CLASS_AVAILABLE(10_10, 8_0)
52 | @interface LMWKThumbnailView : _WKThumbnailView
53 |
54 | @property (strong) LMFakeWKView* fakeWKView;
55 |
56 | + (void)captureScreenshotOfWKWebView:(WKWebView*)wkWebView withCompletionHandler:(CaptureScreenshotCompletionHandler)completionHandler;
57 |
58 | - (instancetype)initWithWKWebView:(WKWebView*)wkWebView completionHandler:(CaptureScreenshotCompletionHandler)completionHandler;
59 | - (void)captureScreenshot;
60 |
61 | @end
62 |
63 | @interface WKWebView (Screenshot)
64 |
65 | - (void)captureScreenshotWithCompletionHandler:(CaptureScreenshotCompletionHandler)completionHandler;
66 | - (NSImage*)captureScreenshotWithTimeout:(dispatch_time_t)timeout;
67 |
68 | @end
69 |
--------------------------------------------------------------------------------
/WKWebViewTest/WKWebView+Screenshot.m:
--------------------------------------------------------------------------------
1 | //
2 | // LMThumbnailView.m
3 | // WKWebViewTest
4 | //
5 | // Created by Felix Deimel on 21.08.15.
6 | // Copyright (c) 2015 Lemon Mojo - Felix Deimel e.U. All rights reserved.
7 | //
8 |
9 | #import "WKWebView+Screenshot.h"
10 |
11 | @implementation LMFakeWKView
12 |
13 | - (instancetype)initWithWebView:(WKWebView*)webView
14 | {
15 | self = [super init];
16 |
17 | if (self) {
18 | self.webView = webView;
19 | }
20 |
21 | return self;
22 | }
23 |
24 | + (instancetype)fakeWKViewWithWebView:(WKWebView*)webView
25 | {
26 | return [[[LMFakeWKView alloc] initWithWebView:webView] autorelease];
27 | }
28 |
29 | - (void)dealloc
30 | {
31 | self.webView = nil;
32 |
33 | [super dealloc];
34 | }
35 |
36 | - (NSView*)_thumbnailView
37 | {
38 | return nil;
39 | }
40 |
41 | - (NSWindow*)window
42 | {
43 | return self.webView.window;
44 | }
45 |
46 | - (void*)pageRef
47 | {
48 | return [self.webView performSelector:@selector(_pageForTesting)];
49 | }
50 |
51 | @end
52 |
53 | @implementation LMWKThumbnailView {
54 | CaptureScreenshotCompletionHandler _completionHandler;
55 | }
56 |
57 | + (void)captureScreenshotOfWKWebView:(WKWebView*)wkWebView withCompletionHandler:(CaptureScreenshotCompletionHandler)completionHandler
58 | {
59 | __block LMWKThumbnailView* thumbnailView = [[LMWKThumbnailView alloc] initWithWKWebView:wkWebView completionHandler:^(NSImage *screenshot) {
60 | [thumbnailView release];
61 |
62 | if (completionHandler) {
63 | completionHandler(screenshot);
64 | }
65 | }];
66 |
67 | [thumbnailView captureScreenshot];
68 | }
69 |
70 | - (instancetype)initWithWKWebView:(WKWebView*)wkWebView completionHandler:(CaptureScreenshotCompletionHandler)completionHandler
71 | {
72 | self.fakeWKView = [LMFakeWKView fakeWKViewWithWebView:wkWebView];
73 |
74 | if (self = [super initWithFrame:NSZeroRect fromWKView:(WKView*)self.fakeWKView]) {
75 | self.exclusivelyUsesSnapshot = YES;
76 | _completionHandler = [completionHandler copy];
77 | }
78 |
79 | return self;
80 | }
81 |
82 | - (void)dealloc
83 | {
84 | [_completionHandler release]; _completionHandler = nil;
85 | self.fakeWKView = nil;
86 |
87 | [super dealloc];
88 | }
89 |
90 | - (void)captureScreenshot
91 | {
92 | [self requestSnapshot];
93 | }
94 |
95 | - (void)_didTakeSnapshot:(CGImageRef)image
96 | {
97 | NSImage *screenshot = nil;
98 |
99 | if (image) {
100 | screenshot = [[[NSImage alloc] initWithCGImage:image size:NSZeroSize] autorelease];
101 | }
102 |
103 | CaptureScreenshotCompletionHandler completionHandler = _completionHandler;
104 |
105 | if (completionHandler) {
106 | completionHandler(screenshot);
107 | }
108 | }
109 |
110 | @end
111 |
112 | @implementation WKWebView (Screenshot)
113 |
114 | - (void)captureScreenshotWithCompletionHandler:(CaptureScreenshotCompletionHandler)completionHandler
115 | {
116 | [LMWKThumbnailView captureScreenshotOfWKWebView:self withCompletionHandler:completionHandler];
117 | }
118 |
119 | - (NSImage*)captureScreenshotWithTimeout:(dispatch_time_t)timeout
120 | {
121 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
122 |
123 | __block NSImage* theScreenshot = nil;
124 |
125 | [self captureScreenshotWithCompletionHandler:^(NSImage *screenshot) {
126 | if (screenshot) {
127 | theScreenshot = [screenshot retain];
128 | }
129 |
130 | dispatch_semaphore_signal(semaphore);
131 | }];
132 |
133 | while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
134 | if (dispatch_time(DISPATCH_TIME_NOW, 0) > timeout) {
135 | theScreenshot = nil;
136 | break;
137 | }
138 |
139 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
140 | }
141 |
142 | return [theScreenshot autorelease];
143 | }
144 |
145 | @end
146 |
--------------------------------------------------------------------------------
/WKWebViewTest/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WKWebViewTest
4 | //
5 | // Created by Felix Deimel on 10.06.15.
6 | // Copyright © 2015 Lemon Mojo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[]) {
12 | return NSApplicationMain(argc, argv);
13 | }
14 |
--------------------------------------------------------------------------------