Skip to content

Commit

Permalink
new version of core screenshot logic
Browse files Browse the repository at this point in the history
  • Loading branch information
RickeyBoy committed Jun 21, 2024
1 parent 6003570 commit c6427ec
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objectVersion = 60;
objects = {

/* Begin PBXBuildFile section */
Expand All @@ -14,11 +14,12 @@
965160DB2A67C17C00B18DD1 /* ScreenshotableViewExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965160DA2A67C17C00B18DD1 /* ScreenshotableViewExampleTests.swift */; };
965160E52A67C17C00B18DD1 /* ScreenshotableViewExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965160E42A67C17C00B18DD1 /* ScreenshotableViewExampleUITests.swift */; };
965160E72A67C17C00B18DD1 /* ScreenshotableViewExampleUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965160E62A67C17C00B18DD1 /* ScreenshotableViewExampleUITestsLaunchTests.swift */; };
965160FF2A67E7F800B18DD1 /* ScreenshotableView in Frameworks */ = {isa = PBXBuildFile; productRef = 965160FE2A67E7F800B18DD1 /* ScreenshotableView */; };
96A33B532C22B4C500A72D16 /* ScrollScreenshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96A33B522C22B4C500A72D16 /* ScrollScreenshotView.swift */; };
96CADB2C2BFCA6CB0074A874 /* ShareSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CADB2B2BFCA6CB0074A874 /* ShareSheetView.swift */; };
96CADB2E2BFCA6EC0074A874 /* SuppotSaveAndShareViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CADB2D2BFCA6EC0074A874 /* SuppotSaveAndShareViewModifier.swift */; };
96CADB302BFCAB630074A874 /* SimpleUseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CADB2F2BFCAB630074A874 /* SimpleUseView.swift */; };
96CADB322BFCAB7B0074A874 /* ShareScreenshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CADB312BFCAB7B0074A874 /* ShareScreenshotView.swift */; };
96F08E7F2C22D010009E030E /* ScreenshotableView in Frameworks */ = {isa = PBXBuildFile; productRef = 96F08E7E2C22D010009E030E /* ScreenshotableView */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -49,6 +50,7 @@
965160E02A67C17C00B18DD1 /* ScreenshotableViewExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenshotableViewExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
965160E42A67C17C00B18DD1 /* ScreenshotableViewExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotableViewExampleUITests.swift; sourceTree = "<group>"; };
965160E62A67C17C00B18DD1 /* ScreenshotableViewExampleUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotableViewExampleUITestsLaunchTests.swift; sourceTree = "<group>"; };
96A33B522C22B4C500A72D16 /* ScrollScreenshotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollScreenshotView.swift; sourceTree = "<group>"; };
96CADB2B2BFCA6CB0074A874 /* ShareSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheetView.swift; sourceTree = "<group>"; };
96CADB2D2BFCA6EC0074A874 /* SuppotSaveAndShareViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuppotSaveAndShareViewModifier.swift; sourceTree = "<group>"; };
96CADB2F2BFCAB630074A874 /* SimpleUseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleUseView.swift; sourceTree = "<group>"; };
Expand All @@ -60,7 +62,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
965160FF2A67E7F800B18DD1 /* ScreenshotableView in Frameworks */,
96F08E7F2C22D010009E030E /* ScreenshotableView in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -109,6 +111,7 @@
965160CB2A67C17B00B18DD1 /* ContentView.swift */,
96CADB2F2BFCAB630074A874 /* SimpleUseView.swift */,
96CADB312BFCAB7B0074A874 /* ShareScreenshotView.swift */,
96A33B522C22B4C500A72D16 /* ScrollScreenshotView.swift */,
96CADB2A2BFCA6AB0074A874 /* Share */,
965160CD2A67C17B00B18DD1 /* Assets.xcassets */,
965160CF2A67C17B00B18DD1 /* Preview Content */,
Expand Down Expand Up @@ -174,7 +177,7 @@
);
name = ScreenshotableViewExample;
packageProductDependencies = (
965160FE2A67E7F800B18DD1 /* ScreenshotableView */,
96F08E7E2C22D010009E030E /* ScreenshotableView */,
);
productName = ScreenshotableViewExample;
productReference = 965160C62A67C17B00B18DD1 /* ScreenshotableViewExample.app */;
Expand Down Expand Up @@ -249,7 +252,7 @@
);
mainGroup = 965160BD2A67C17B00B18DD1;
packageReferences = (
965160FD2A67E7F800B18DD1 /* XCRemoteSwiftPackageReference "ScreenshotableView" */,
96F08E7D2C22D010009E030E /* XCLocalSwiftPackageReference ".." */,
);
productRefGroup = 965160C72A67C17B00B18DD1 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -297,6 +300,7 @@
96CADB2E2BFCA6EC0074A874 /* SuppotSaveAndShareViewModifier.swift in Sources */,
96CADB322BFCAB7B0074A874 /* ShareScreenshotView.swift in Sources */,
96CADB302BFCAB630074A874 /* SimpleUseView.swift in Sources */,
96A33B532C22B4C500A72D16 /* ScrollScreenshotView.swift in Sources */,
96CADB2C2BFCA6CB0074A874 /* ShareSheetView.swift in Sources */,
965160CA2A67C17B00B18DD1 /* ScreenshotableViewExampleApp.swift in Sources */,
);
Expand Down Expand Up @@ -631,21 +635,16 @@
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
965160FD2A67E7F800B18DD1 /* XCRemoteSwiftPackageReference "ScreenshotableView" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/RickeyBoy/ScreenshotableView";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
};
/* Begin XCLocalSwiftPackageReference section */
96F08E7D2C22D010009E030E /* XCLocalSwiftPackageReference ".." */ = {
isa = XCLocalSwiftPackageReference;
relativePath = ..;
};
/* End XCRemoteSwiftPackageReference section */
/* End XCLocalSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
965160FE2A67E7F800B18DD1 /* ScreenshotableView */ = {
96F08E7E2C22D010009E030E /* ScreenshotableView */ = {
isa = XCSwiftPackageProductDependency;
package = 965160FD2A67E7F800B18DD1 /* XCRemoteSwiftPackageReference "ScreenshotableView" */;
productName = ScreenshotableView;
};
/* End XCSwiftPackageProductDependency section */
Expand Down

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ struct ContentView: View {
}
}
Spacer()
NavigationLink(destination: ScrollScreenshotView()) {
VStack {
Image(systemName: "scroll")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20)
.padding()
Text("Screenshot For ScrollView")
.font(.headline)
}
}
Spacer()
}
.padding()
.navigationBarTitle("Screenshot!")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// ScrollScreenshotView.swift
// ScreenshotableViewExample
//
// Created by Wang Timo on 2024/6/19.
//

import SwiftUI
import ScreenshotableView

/// 生成随机颜色
/// generate random color
extension Color {
static var random: Color {
return Color(red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1))
}
}

struct ScrollScreenshotView: View {
@State var shotting = false
@State var screenshot: Image? = nil

let colors = generateRandomColors()

var body: some View {
VStack(spacing: 40) {
if let screenshot {
Text("Screenshot result ↓")
screenshot
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 400)
} else {
Text("Scrollview ↓")
ScreenshotableContent()
.frame(height: 400)
}

Button("Generate Screenshot") {
shotting.toggle()
}
Button("Clear Screenshot") {
screenshot = nil
}
}
}

private func ScreenshotableContent() -> some View {
ScreenshotableScrollView(shotting: $shotting) { screenshot in
self.screenshot = Image(uiImage: screenshot)
} content: { style in
VStack {
ForEach(0..<colors.count, id: \.self) { index in
Rectangle()
.fill(colors[index])
.frame(height: 100)
.padding(2)
}
}
.padding()
.border(.black)
.padding()
}
}

static func generateRandomColors(count: Int = 10) -> [Color] {
return (0..<count).map { _ in Color.random }
}
}

#Preview {
ScrollScreenshotView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct SimpleUseView: View {

if let screenshot {
VStack(spacing: 20) {
Text("snapshot result:")
Text("screenshot result:")
screenshot
.frame(height: 60)
}
Expand Down
48 changes: 46 additions & 2 deletions Sources/ScreenshotableView/ScreenshotableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ public enum ScreenshotableViewStyle {
case inView
}

/// ScrollView supports screenshot
public struct ScreenshotableScrollView<Content: View>: View {
@Binding var shotting: Bool
var completed: (UIImage) -> Void
let content: (_ style: ScreenshotableViewStyle) -> Content

public init(shotting:Binding<Bool>, completed: @escaping (UIImage) -> Void, @ViewBuilder content: @escaping (_ style: ScreenshotableViewStyle) -> Content) {
self._shotting = shotting
self.completed = completed
self.content = content
}

public var body: some View {
ScrollView {
ScreenshotableCotent(shotting: $shotting, completed: completed, content: content, isinScrollView: true)
}
}
}

/// ScrollView supports screenshot
public struct ScreenshotableView<Content: View>: View {
@Binding var shotting: Bool
var completed: (UIImage) -> Void
Expand All @@ -29,10 +49,34 @@ public struct ScreenshotableView<Content: View>: View {
}

public var body: some View {

ZStack {
ScreenshotableCotent(shotting: $shotting, completed: completed, content: content, isinScrollView: true)
}
}
}

/// Normal View supports screenshot
public struct ScreenshotableCotent<Content: View>: View {
@Binding public var shotting: Bool
var completed: (UIImage) -> Void
let content: (_ style: ScreenshotableViewStyle) -> Content
var isInScrollView: Bool = false

public init(shotting:Binding<Bool>, completed: @escaping (UIImage) -> Void, @ViewBuilder content: @escaping (_ style: ScreenshotableViewStyle) -> Content) {
self._shotting = shotting
self.completed = completed
self.content = content
}

init(shotting:Binding<Bool>, completed: @escaping (UIImage) -> Void, @ViewBuilder content: @escaping (_ style: ScreenshotableViewStyle) -> Content, isinScrollView: Bool) {
self.init(shotting: shotting, completed: completed, content: content)
self.isInScrollView = isinScrollView
}

public var body: some View {
func internalView(proxy: GeometryProxy) -> some View {
if self.shotting {
let frame = proxy.frame(in: .global)
let frame = proxy.frame(in: isInScrollView ? .local : .global)
DispatchQueue.main.async {
shotting = false
// 截图时用 inSnapshot 的样式
Expand Down
36 changes: 21 additions & 15 deletions Sources/ScreenshotableView/ScreenshotableViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,11 @@
import SwiftUI

extension UIView {
func takeScreenshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, UIScreen.main.scale)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return capturedImage
}

func takeScreenshot(afterScreenUpdates: Bool) -> UIImage {
if !self.responds(to: #selector(drawHierarchy(in:afterScreenUpdates:))) {
return self.takeScreenshot()
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, UIScreen.main.scale)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates)
let snapshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshot!
}
}

Expand All @@ -33,10 +21,22 @@ extension View {
let hosting = UIHostingController(rootView: self)
hosting.overrideUserInterfaceStyle = UIApplication.shared.currentUIWindow()?.overrideUserInterfaceStyle ?? .unspecified
hosting.view.frame = frame
hosting.view.backgroundColor = .yellow
return hosting.view.takeScreenshot(afterScreenUpdates: afterScreenUpdates)
}
}

extension UIHostingController {
func ignoreSafeArea() {
if #available(iOS 16.4, *) {
self.safeAreaRegions = []
} else {
let currentSafeAreaInset = UIApplication.shared.currentUIWindow()?.safeAreaInsets ?? .zero
self.additionalSafeAreaInsets = currentSafeAreaInset.reversed()
}
}
}

extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
Expand All @@ -51,3 +51,9 @@ extension UIApplication {
}
}

extension UIEdgeInsets {
func reversed() -> UIEdgeInsets {
return UIEdgeInsets(top: -top, left: -left, bottom: -bottom, right: -right)
}
}

0 comments on commit c6427ec

Please sign in to comment.