Skip to content

Instantly share code, notes, and snippets.

@pabloruan0710
Last active August 28, 2023 10:34
Show Gist options
  • Select an option

  • Save pabloruan0710/2c58f31767ff56d6949dbefa45ab6ced to your computer and use it in GitHub Desktop.

Select an option

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

xyxc0673 commented Aug 28, 2023 •

Copy link
Copy Markdown

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