Last active
August 28, 2023 10:34
-
-
Save pabloruan0710/2c58f31767ff56d6949dbefa45ab6ced to your computer and use it in GitHub Desktop.
Create Snapshot for ScrollView (Views) SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HostSnapshoptViewController.swift | |
// git:pabloruan0710 | |
// | |
// Created by Pablo Ruan on 28/04/23. | |
// Copyright © 2023 Pablo Ruan. All rights reserved. | |
// | |
import Foundation | |
import SwiftUI | |
import UIKit | |
/** | |
For use in SwiftUI: | |
ScrollView { | |
}.onTapGesture { | |
let image = self.snapshot() | |
shareItem = [image] | |
} | |
*/ | |
class HostSnapshoptViewController<Content>: UIHostingController<Content> where Content: View { | |
private lazy var scrollView: UIScrollView = { | |
let scrollView = UIScrollView() | |
scrollView.translatesAutoresizingMaskIntoConstraints = false | |
return scrollView | |
}() | |
private lazy var stackView: UIStackView = { | |
let stack = UIStackView() | |
stack.axis = .vertical | |
stack.distribution = .fillProportionally | |
stack.translatesAutoresizingMaskIntoConstraints = false | |
return stack | |
}() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
buildViewHierarchy() | |
addConstraints() | |
addChildView() | |
} | |
private func buildViewHierarchy() { | |
view.addSubview(scrollView) | |
scrollView.addSubview(stackView) | |
} | |
private func addConstraints() { | |
NSLayoutConstraint.activate([ | |
scrollView.topAnchor.constraint(equalTo: view.topAnchor), | |
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | |
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | |
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), | |
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor), | |
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), | |
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), | |
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), | |
stackView.widthAnchor.constraint(equalTo: view.widthAnchor) | |
]) | |
} | |
private func addChildView() { | |
let child = UIHostingController(rootView: rootView) | |
addChildViewController(child) | |
if let view = child.view { | |
view.translatesAutoresizingMaskIntoConstraints = false | |
stackView.addArrangedSubview(view) | |
} | |
child.didMove(toParent: self) | |
scrollView.layoutIfNeeded() | |
} | |
// MARK: Public APIs | |
public func getSize() -> CGSize { | |
scrollView.layoutIfNeeded() | |
return scrollView.contentSize | |
} | |
public func snapshot() -> UIImage { | |
let savedContentOffset = scrollView.contentOffset | |
scrollView.contentOffset = CGPoint.zero | |
let image = scrollView.asImage() | |
scrollView.contentOffset = savedContentOffset | |
return image | |
} | |
} | |
extension View { | |
func snapshot() -> UIImage { | |
let controller = HostSnapshoptViewController(rootView: self) | |
let targetSize = controller.getSize() | |
let bounds = CGRect(origin: .zero, size: targetSize) | |
let window = UIWindow(frame: bounds) | |
window.rootViewController = controller | |
window.makeKeyAndVisible() | |
let image = controller.snapshot() | |
return image | |
} | |
} | |
extension UIView { | |
func asImage() -> UIImage { | |
let renderer = UIGraphicsImageRenderer(bounds: bounds) | |
return renderer.image { rendererContext in | |
layer.render(in: rendererContext.cgContext) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It seems that's the max height of image is limited to the screen height because only 38 indexes are visible with the code below:
iOS 17 Public beta and Xcode-15-beta 7