오프 스크린 부분을 포함하여 UIScrollView의 스크린 샷 얻기
나는 UIScrollView
다음과 같은 takeScreenshot 메소드를 구현하는 decentdent가 있습니다.
-(void)takeScreenshot {
CGRect contextRect = CGRectMake(0, 0, 768, 1004);
UIGraphicsBeginImageContext(contextRect.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// do something with the viewImage here.
}
이것은 기본적으로 스크롤 뷰의 맨 위로 이동하고 보이는 영역의 스크린 샷을 찍습니다. iPad가 세로 방향 일 때는 잘 작동하지만 가로 방향 일 때는 이미지 하단이 잘립니다 (가시 영역의 높이가 1004가 아닌 748에 불과하므로).
UIScrollView
화면에없는 영역을 포함하여 의 스냅 샷을 얻을 수 있습니까? 아니면보기를 아래로 스크롤하고 두 번째 사진을 찍은 다음 서로 연결해야합니까?
작동하는 코드는 다음과 같습니다.
- (IBAction) renderScrollViewToImage
{
UIImage* image = nil;
UIGraphicsBeginImageContext(_scrollView.contentSize);
{
CGPoint savedContentOffset = _scrollView.contentOffset;
CGRect savedFrame = _scrollView.frame;
_scrollView.contentOffset = CGPointZero;
_scrollView.frame = CGRectMake(0, 0, _scrollView.contentSize.width, _scrollView.contentSize.height);
[_scrollView.layer renderInContext: UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
_scrollView.contentOffset = savedContentOffset;
_scrollView.frame = savedFrame;
}
UIGraphicsEndImageContext();
if (image != nil) {
[UIImagePNGRepresentation(image) writeToFile: @"/tmp/test.png" atomically: YES];
system("open /tmp/test.png");
}
}
마지막 몇 줄은 단순히 이미지를 /tmp/test.png에 쓴 다음 Preview.app에서 엽니 다. 이것은 분명히 시뮬레이터에서만 작동합니다 :-)
ScrollViewScreenShot Github 저장소 에서 프로젝트 완료
저에게는 현재 Stefan Arentz의 답변이 작동하지 않았습니다.
나는 이것을 iOS 8 이상에서 구현해야했고 iPhone에서 테스트했습니다. 허용되는 답변은 스크롤 뷰의 보이는 부분을 렌더링하고 나머지 이미지는 비어 있습니다.
나는 이것을 사용하여 고쳐 보았습니다 drawViewHierarchyInRect
-운 이 없습니다. 에 따라 afterScreenUpdates
인 true
또는 false
나는 이미지 나 내용의 부분의 일부를 뻗어있어.
UIScrollView
의 전체 콘텐츠 에 대한 정확한 스냅 샷을 얻을 수있는 유일한 방법 은 다른 임시보기에 추가 한 다음 렌더링하는 것입니다.
샘플 코드는 다음과 같습니다 ( scrollview
내 VC의 콘센트).
func getImageOfScrollView() -> UIImage {
var image = UIImage()
UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.mainScreen().scale)
// save initial values
let savedContentOffset = self.scrollView.contentOffset
let savedFrame = self.scrollView.frame
let savedBackgroundColor = self.scrollView.backgroundColor
// reset offset to top left point
self.scrollView.contentOffset = CGPointZero
// set frame to content size
self.scrollView.frame = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height)
// remove background
self.scrollView.backgroundColor = UIColor.clearColor()
// make temp view with scroll view content size
// a workaround for issue when image on ipad was drawn incorrectly
let tempView = UIView(frame: CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height))
// save superview
let tempSuperView = self.scrollView.superview
// remove scrollView from old superview
self.scrollView.removeFromSuperview()
// and add to tempView
tempView.addSubview(self.scrollView)
// render view
// drawViewHierarchyInRect not working correctly
tempView.layer.renderInContext(UIGraphicsGetCurrentContext())
// and get image
image = UIGraphicsGetImageFromCurrentImageContext()
// and return everything back
tempView.subviews[0].removeFromSuperview()
tempSuperView?.addSubview(self.scrollView)
// restore saved settings
self.scrollView.contentOffset = savedContentOffset
self.scrollView.frame = savedFrame
self.scrollView.backgroundColor = savedBackgroundColor
UIGraphicsEndImageContext()
return image
}
UIScrollView를 처리하는 UIView 확장의 작업 예 :
extension UIView {
func screenshot() -> UIImage {
if(self is UIScrollView) {
let scrollView = self as! UIScrollView
let savedContentOffset = scrollView.contentOffset
let savedFrame = scrollView.frame
UIGraphicsBeginImageContext(scrollView.contentSize)
scrollView.contentOffset = .zero
self.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
scrollView.contentOffset = savedContentOffset
scrollView.frame = savedFrame
return image!
}
UIGraphicsBeginImageContext(self.bounds.size)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
@Roopesh Mittal의 답변 에서이 솔루션을 가져와 더 안전하고 깨끗하게 만들었습니다.
Swift 4 호환
fileprivate extension UIScrollView {
func screenshot() -> UIImage? {
let savedContentOffset = contentOffset
let savedFrame = frame
UIGraphicsBeginImageContext(contentSize)
contentOffset = .zero
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
contentOffset = savedContentOffset
frame = savedFrame
return image
}
}
@RyanG의 답변을 기반으로 한 세련된 Swift 4.x / 5.0 버전 :
fileprivate extension UIScrollView {
func screenshot() -> UIImage? {
// begin image context
UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
// save the orginal offset & frame
let savedContentOffset = contentOffset
let savedFrame = frame
// end ctx, restore offset & frame before returning
defer {
UIGraphicsEndImageContext()
contentOffset = savedContentOffset
frame = savedFrame
}
// change the offset & frame so as to include all content
contentOffset = .zero
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
SWIFT 3 버전 :
func snapshot() -> UIImage?
{
UIGraphicsBeginImageContext(scrollView.contentSize)
let savedContentOffset = scrollView.contentOffset
let savedFrame = scrollView.frame
scrollView.contentOffset = CGPoint.zero
scrollView.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
scrollView.contentOffset = savedContentOffset
scrollView.frame = savedFrame
UIGraphicsEndImageContext()
return image
}
이것은 나를 위해 일했습니다.
확대 / 축소 수준을 고려하는 또 다른 방법이 있습니다. 4 개의 다른 UIImageView 레이어가있는 scrollview가 있고 현재 상태의 스크린 샷을 찍고 싶습니다.
float theScale = 1.0f / theScrollView.zoomScale;
// The viewing rectangle in absolute coordinates
CGRect visibleArea = CGRectMake((int)(theScrollView.contentOffset.x * theScale), (int)(theScrollView.contentOffset.y * theScale),
(int)(theScrollView.bounds.size.width * theScale), (int)(theScrollView.bounds.size.height * theScale));
NSArray *layers = [NSArray arrayWithObjects:imageLayer1, imageLayer2, imageLayer3, imageLayer4, nil];
UIGraphicsBeginImageContext(visibleArea.size);
for (UIImageView *layer in layers) {
CALayer *coreLayer = layer.layer;
coreLayer.bounds = CGRectMake(layer.frame.origin.x - visibleArea.origin.x, layer.frame.origin.y - visibleArea.origin.y, layer.frame.size.width, layer.frame.size.height);
[coreLayer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
절대 좌표로 스크린 샷을 찍습니다. 즉, 스크롤 뷰에 2048 * 2048 이미지가 있고 그 중 약 1/4을 볼 수 있다면 화면 해상도에 관계없이 512 * 512의 스크린 샷이 찍 힙니다. 화면 해상도 (예 : 320 * 480)로 스크린 샷을 찍으려면 위 코드 바로 뒤에 다음과 같이 이미지를 조정해야합니다.
UIGraphicsBeginImageContext(theScrollView.frame.size);
[screenshot drawInRect:CGRectMake(0, 0, theScrollView.frame.size.width, theScrollView.frame.size.height)];
UIImage *smallScreenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
전체 화면을 넘어서 스크롤보기를 확장하고 싶지 않다면 (어쨌든 자동 레이아웃에서 작동하지 않음) 더 나은 방법이 있습니다.
contentOffset
스크롤 뷰 와 함께 핵심 그래픽 변환 을 사용하여 동일한 작업을 수행 할 수 있습니다.
//
// ScrollViewSnapshotter.swift
// ScrollViewSnapshotter
//
// Created by Moshe Berman on 4/10/16.
// Copyright © 2016 Moshe Berman. All rights reserved.
//
import UIKit
class ScrollViewSnapshotter: NSObject {
func PDFWithScrollView(scrollview: UIScrollView) -> NSData {
/**
* Step 1: The first thing we need is the default origin and size of our pages.
* Since bounds always start at (0, 0) and the scroll view's bounds give us
* the correct size for the visible area, we can just use that.
*
* In the United States, a standard printed page is 8.5 inches by 11 inches,
* but when generating a PDF it's simpler to keep the page size matching the
* visible area of the scroll view. We can let our printer software (such
* as the Preview app on OS X or the Printer app on iOS) do the scaling.
*
* If we wanted to scale ourselves, we could multiply each of those
* numbers by 72, to get the number of points for each dimension.
* We would have to change how we generated the the pages below, so
* for simplicity, we're going to stick to one page per screenful of content.
*/
let pageDimensions = scrollview.bounds
/**
* Step 2: Now we need to know how many pages we will need to fit our content.
* To get this, we divide our scroll views dimensions by the size
* of each page, in either direction.
* We also need to round up, so that the pages don't get clipped.
*/
let pageSize = pageDimensions.size
let totalSize = scrollview.contentSize
let numberOfPagesThatFitHorizontally = Int(ceil(totalSize.width / pageSize.width))
let numberOfPagesThatFitVertically = Int(ceil(totalSize.height / pageSize.height))
/**
* Step 3: Set up a Core Graphics PDF context.
*
* First we create a backing store for the PDF data, then
* pass it and the page dimensions to Core Graphics.
*
* We could pass in some document information here, which mostly cover PDF metadata,
* including author name, creator name (our software) and a password to
* require when viewing the PDF file.
*
* Also note that we can use UIGraphicsBeginPDFContextToFile() instead,
* which writes the PDF to a specified path. I haven't played with it, so
* I don't know if the data is written all at once, or as each page is closed.
*/
let outputData = NSMutableData()
UIGraphicsBeginPDFContextToData(outputData, pageDimensions, nil)
/**
* Step 4: Remember some state for later.
* Then we need to clear the content insets, so that our
* core graphics layer and our content offset match up.
* We don't need to reset the content offset, because that
* happens implicitly, in the loop below.
*/
let savedContentOffset = scrollview.contentOffset
let savedContentInset = scrollview.contentInset
scrollview.contentInset = UIEdgeInsetsZero
/**
* Step 6: Now we loop through the pages and generate the data for each page.
*/
if let context = UIGraphicsGetCurrentContext()
{
for indexHorizontal in 0 ..< numberOfPagesThatFitHorizontally
{
for indexVertical in 0 ..< numberOfPagesThatFitVertically
{
/**
* Step 6a: Start a new page.
*
* This automatically closes the previous page.
* There's a similar method UIGraphicsBeginPDFPageWithInfo,
* which allows you to configure the rectangle of the page and
* other metadata.
*/
UIGraphicsBeginPDFPage()
/**
* Step 6b:The trick here is to move the visible portion of the
* scroll view *and* adjust the core graphics context
* appropriately.
*
* Consider that the viewport of the core graphics context
* is attached to the top of the scroll view's content view
* and we need to push it in the opposite direction as we scroll.
* Further, anything not inside of the visible area of the scroll
* view is clipped, so scrolling will move the core graphics viewport
* out of the rendered area, producing empty pages.
*
* To counter this, we scroll the next screenful into view, and adjust
* the core graphics context. Note that core graphics uses a coordinate
* system which has the y coordinate decreasing as we go from top to bottom.
* This is the opposite of UIKit (although it matches AppKit on OS X.)
*/
let offsetHorizontal = CGFloat(indexHorizontal) * pageSize.width
let offsetVertical = CGFloat(indexVertical) * pageSize.height
scrollview.contentOffset = CGPointMake(offsetHorizontal, offsetVertical)
CGContextTranslateCTM(context, -offsetHorizontal, -offsetVertical) // NOTE: Negative offsets
/**
* Step 6c: Now we are ready to render the page.
*
* There are faster ways to snapshot a view, but this
* is the most straightforward way to render a layer
* into a context.
*/
scrollview.layer.renderInContext(context)
}
}
}
/**
* Step 7: End the document context.
*/
UIGraphicsEndPDFContext()
/**
* Step 8: Restore the scroll view.
*/
scrollview.contentInset = savedContentInset
scrollview.contentOffset = savedContentOffset
/**
* Step 9: Return the data.
* You can write it to a file, or display it the user,
* or even pass it to iOS for sharing.
*/
return outputData
}
}
프로세스를 설명 하는 블로그 게시물 이 있습니다.
PDF를 생성하는 프로세스는 페이지 대신 스크롤보기의 크기와 일치하는 하나의 큰 캔버스를 만든 다음 청크로 콘텐츠를 가져와야한다는 점을 제외하고는 이미지 스냅 샷과 매우 유사합니다.
@gleb vodovozov 덕분에 SWIFT 3 버전 :
func getImageOfScrollView()->UIImage{
var image = UIImage();
UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.main.scale)
// save initial values
let savedContentOffset = self.scrollView.contentOffset;
let savedFrame = self.scrollView.frame;
let savedBackgroundColor = self.scrollView.backgroundColor
// reset offset to top left point
self.scrollView.contentOffset = CGPoint.zero;
// set frame to content size
self.scrollView.frame = CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height)
// remove background
self.scrollView.backgroundColor = UIColor.clear
// make temp view with scroll view content size
// a workaround for issue when image on ipad was drawn incorrectly
let tempView = UIView(frame: CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height))
// save superview
let tempSuperView = self.scrollView.superview
// remove scrollView from old superview
self.scrollView.removeFromSuperview()
// and add to tempView
tempView.addSubview(self.scrollView)
// render view
// drawViewHierarchyInRect not working correctly
tempView.layer.render(in: UIGraphicsGetCurrentContext()!)
// and get image
image = UIGraphicsGetImageFromCurrentImageContext()!;
// and return everything back
tempView.subviews[0].removeFromSuperview()
tempSuperView?.addSubview(self.scrollView)
// restore saved settings
self.scrollView.contentOffset = savedContentOffset;
self.scrollView.frame = savedFrame;
self.scrollView.backgroundColor = savedBackgroundColor
UIGraphicsEndImageContext();
return image
}
잘 모르겠지만 contextRect
풍경 에 대해 이와 같은 크기를 설정하면 잘 작동 할 것이라고 추측 할 수 있습니다.
CGRect contextRect = CGRectMake(0, 0, 1004, 768*2);
Because this contextRect will determine the size of the UIGraphicsBeginImageContext
so I hope that double the height can solve your problem
ReferenceURL : https://stackoverflow.com/questions/3539717/getting-a-screenshot-of-a-uiscrollview-including-offscreen-parts
'IT TIP' 카테고리의 다른 글
ASP.NET 리피터 대체 행 강조 표시 (0) | 2021.01.09 |
---|---|
Maven에서 Version.java 파일 생성 (0) | 2021.01.09 |
SQL을 사용하지 않고 Magento 설정 스크립트의 ALTER TABLE (0) | 2021.01.09 |
교리 및 LIKE 쿼리 (0) | 2021.01.09 |
WCF 웹 서비스 오류 : ASP.NET 호환성을 지원하지 않으므로 서비스를 활성화 할 수 없습니다. (0) | 2021.01.09 |