Skip to content

Instantly share code, notes, and snippets.

@pabloruan0710
Last active August 28, 2023 10:34
Show Gist options
  • Save pabloruan0710/2c58f31767ff56d6949dbefa45ab6ced to your computer and use it in GitHub Desktop.
Save pabloruan0710/2c58f31767ff56d6949dbefa45ab6ced to your computer and use it in GitHub Desktop.
Create Snapshot for ScrollView (Views) SwiftUI
//
// 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)
}
}
}
@xyxc0673
Copy link

xyxc0673 commented Aug 28, 2023

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:

let image = ScrollView{
    ForEach(1..<100) { index in
            Text("index: \(index)")
    }
}.snapshot()

iOS 17 Public beta and Xcode-15-beta 7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment