Retina 디스플레이에서 품질 손실없이 UIView를 UIImage로 캡처하는 방법


303

내 코드는 일반적인 장치에서는 잘 작동하지만 망막 장치에서 흐릿한 이미지를 만듭니다.

아무도 내 문제에 대한 해결책을 알고 있습니까?

+ (UIImage *) imageWithView:(UIView *)view
{
    UIGraphicsBeginImageContext(view.bounds.size);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}

흐릿합니다. 그것은 올바른 규모를 잃어버린 것 같습니다 ...
Daniel

나도. 같은 문제를 만났습니다.
RainCast

결과 이미지를 어디에 표시합니까? 다른 사람들이 설명했듯이 장치가 크기가 2 또는 3 인 동안 크기가 1 인 이미지로보기를 렌더링한다고 생각합니다. 따라서 해상도가 낮아집니다. 품질이 떨어지지 만 흐려서는 안됩니다. 그러나 크기가 2 인 장치 에서이 이미지를 다시 (동일한 화면에서) 볼 때 스크린 샷의 픽셀 당 2 픽셀을 사용하여 저해상도에서 더 높은 해상도로 변환됩니다. 이 업 스케일링은 보간을 가장 많이 사용하며 (iOS의 기본값) 추가 픽셀의 평균 색상 값입니다.
Hans Terje Bakke

답변:


667

사용 UIGraphicsBeginImageContext에서 UIGraphicsBeginImageContextWithOptions( 이 페이지에 설명 대로)로 전환 하십시오 . scale (세 번째 인수)에 0.0을 전달하면 화면의 배율과 동일한 배율을 가진 컨텍스트가 표시됩니다.

UIGraphicsBeginImageContext고정 배율 1.0을 사용하므로 실제로 iPhone 4에서 다른 iPhone과 정확히 동일한 이미지를 얻습니다. 필자는 아이폰 4가 암묵적으로 확장했을 때 필터를 적용하거나 내 두뇌가 주변의 모든 것보다 덜 선명하게 필터를 걸고 있다고 생각합니다.

그래서, 나는 추측한다 :

#import <QuartzCore/QuartzCore.h>

+ (UIImage *)imageWithView:(UIView *)view
{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}

그리고 스위프트 4에서 :

func image(with view: UIView) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
    defer { UIGraphicsEndImageContext() }
    if let context = UIGraphicsGetCurrentContext() {
        view.layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        return image
    }
    return nil
}

6
Tommy의 대답은 문제가 없지만 경고 #import <QuartzCore/QuartzCore.h>를 제거 하려면 여전히 가져와야 합니다 renderInContext:.
gwdp

2
@WayneLiu 명시 적으로 배율을 2.0으로 지정할 수 있지만로드 된 비트 맵이 @ 2x 버전이 아닌 표준 버전이기 때문에 iPhone 4 품질 이미지를 얻지 못할 수 있습니다. 나는 현재 UIImage재로드 를 보류하고 있지만 고해상도 버전을 강제 로 지시하는 모든 방법을 지시 할 수있는 방법이 없기 때문에 그 해결책이 있다고 생각하지 않습니다. -어쨌든 망막 장치).
Tommy

6
0.0fscale 매개 변수에 for 를 사용하는 대신을 사용하는 것이 더 허용 [[UIScreen mainScreen] scale]가능한가요?
Adam Carter

20
@Adam Carter scale: The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.명시 적으로 문서화되어 있으므로 0.0f는 더 간단하고 좋습니다.
cprcrack

5
이 답변은 iOS 7에서 오래되었습니다.이를위한 새로운 "최상의"방법은 제 답변을 참조하십시오.
Dima

224

현재 iOS 7을 지원하는 경우 현재 허용되는 답변이 최신 상태가 아닙니다.

iOS7 + 만 지원하는 경우 사용해야하는 내용은 다음과 같습니다.

+ (UIImage *) imageWithView:(UIView *)view
{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f);
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
    UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snapshotImage;
}

스위프트 4 :

func imageWithView(view: UIView) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
    defer { UIGraphicsEndImageContext() }
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
    return UIGraphicsGetImageFromCurrentImageContext()
}

에 따라 이 문서 새 iOS7에 방법이 있음을 알 수 drawViewHierarchyInRect:afterScreenUpdates:많은 시간보다 더 빨리이다 renderInContext:.기준


5
의심 할 여지없이, 그것은 drawViewHierarchyInRect:afterScreenUpdates:훨씬 빠릅니다. 방금 Instruments에서 Time profiler를 실행했습니다. 내 이미지 생성은 138ms에서 27ms로 진행되었습니다.
ThomasCle

9
Google지도 아이콘에 대한 맞춤 아이콘을 만들려고 할 때 어떤 이유로 든 작동하지 않았습니다. 내가 가진 전부는 검은 사각형이었습니다 :(
JakeP

6
당신이 설정 시도가 @CarlosP afterScreenUpdates:YES?
Dima

7
afterScreenUpdates를 YES로 설정하면 검은 사각형 문제가 해결되었습니다.
hyouuu

4
나는 또한 검은 이미지를 받고 있었다. 코드를 호출 viewDidLoad하거나 viewWillAppear:이미지가 검은 색인 것으로 나타났습니다 . 나는 그것을해야했다 viewDidAppear:. 그래서 나는 또한 마침내로 돌아갔다 renderInContext:.
manicaesar

32

@Dima 솔루션을 기반으로 Swift 확장을 만들었습니다.

extension UIImage {
    class func imageWithView(view: UIView) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
        view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }
}

편집 : Swift 4 개선 버전

extension UIImage {
    class func imageWithView(_ view: UIView) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0)
        defer { UIGraphicsEndImageContext() }
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
        return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
    }
}

용법:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))  
let image = UIImage.imageWithView(view)

2
아이디어 주셔서 감사합니다! 따로, 컨텍스트를 시작한 직후 {UIGraphicsEndImageContext ()}를 연기하고 로컬 변수 img를 도입하지 않아도됩니다.)
Dennis L

자세한 설명과 사용법에 대해 대단히 감사합니다!
Zeus

왜 이것이 class기능을 하는 기능입니까?
Rudolf Adamkovič

이것은 static함수 처럼 작동하지만 재정의 할 필요가 없으므로 static대신 대신 func로 변경할 수 있습니다 class.
Heberti Almeida

25

iOS 빠른

최신 UIGraphicsImageRenderer 사용

public extension UIView {
    @available(iOS 10.0, *)
    public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage {
        let rendererFormat = UIGraphicsImageRendererFormat.default()
        rendererFormat.opaque = isOpaque
        let renderer = UIGraphicsImageRenderer(size: bounds.size, format: rendererFormat)

        let snapshotImage = renderer.image { _ in
            drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
        }
        return snapshotImage
    }
}

@ masaldana2는 아직은 아니지만, 더 이상 사용되지 않거나 하드웨어 가속 렌더링 또는 개선 기능을 추가하자마자 거대 어깨 (마지막 Apple API를 사용하는 AKA)를 사용하는 것이 좋습니다
Juan Boero

누구든지 이것을 renderer이전 과 비교하여 성능이 무엇인지 알고 drawHierarchy있습니까?
Vive

24

@Tommy 및 @Dima의 답변을 향상 시키려면 다음 범주를 사용하여 UIView를 투명한 배경 과 품질 손실없이 UIImage로 렌더링하십시오 . iOS7에서 작업 중입니다. (또는 self참조를 이미지로 대체하여 구현에서 해당 메소드를 재사용 하십시오)

UIView + RenderViewToImage.h

#import <UIKit/UIKit.h>

@interface UIView (RenderToImage)

- (UIImage *)imageByRenderingView;

@end

UIView + RenderViewToImage.m

#import "UIView+RenderViewToImage.h"

@implementation UIView (RenderViewToImage)

- (UIImage *)imageByRenderingView
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
    UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snapshotImage;
}

@end

1
afterScreenUpdates : YES와 함께 drawViewHierarchyInRect를 사용하면 이미지 종횡비가 변경되고 이미지가 왜곡됩니다.
confile

UIView 컨텐츠가 변경 될 때 이런 일이 발생합니까? 그렇다면이 UIImage를 다시 생성 해보십시오. 내가 전화 중이기 때문에 이것을 직접 테스트 할 수 없습니다 죄송합니다
Glogo

나는 같은 문제가 있으며 아무도 이것을 알아 차리지 못한 것 같습니다.이 문제에 대한 해결책을 찾았습니까? @ 파일?
Reza.Ab

이것은 내가 필요로하는 것이지만 작동하지 않았습니다. 나는 시도 @interface하고 @implementation둘 다 시도하고 헤더에 (RenderViewToImage)추가 했으므로 이것이 올바른 사용법입니까? 예를 들어 ? #import "UIView+RenderViewToImage.h"ViewController.hUIImageView *test = [[UIImageView alloc] initWithImage:[self imageByRenderingView]]; [self addSubview:test];
그렉

이 방법은 ViewController.m제가 렌더링하려고했던 관점이었습니다- (UIView *)testView { UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)]; v1.backgroundColor = [UIColor redColor]; UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 150, 150)]; v2.backgroundColor = [UIColor blueColor]; [v2 addSubview:v1]; return v2; }
Greg Greg

15

스위프트 3

스위프트 3 (을 기반으로 솔루션 디마의 대답 UIView의 확장자를 가진)은 다음과 같이해야합니다 :

extension UIView {
    public func getSnapshotImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0)
        self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
        let snapshotImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return snapshotImage
    }
}

6

새로운 iOS 10.0 API 및 이전 방법을 지원하는 Drop-in Swift 3.0 확장.

노트 :

  • iOS 버전 확인
  • 컨텍스트 정리를 단순화하기 위해 지연을 사용하십시오.
  • 뷰의 불투명도 및 현재 배율을 적용합니다.
  • 래핑되지 않은 !것은 크래시를 일으킬 수 있습니다.

extension UIView
{
    public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage?
    {
        if #available(iOS 10.0, *)
        {
            let rendererFormat = UIGraphicsImageRendererFormat.default()
            rendererFormat.scale = self.layer.contentsScale
            rendererFormat.opaque = self.isOpaque
            let renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: rendererFormat)

            return
                renderer.image
                {
                    _ in

                    self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates)
                }
        }
        else
        {
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, self.layer.contentsScale)
            defer
            {
                UIGraphicsEndImageContext()
            }

            self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates)

            return UIGraphicsGetImageFromCurrentImageContext()
        }
    }
}

5

스위프트 2.0 :

확장 방법 사용 :

extension UIImage{

   class func renderUIViewToImage(viewToBeRendered:UIView?) -> UIImage
   {
       UIGraphicsBeginImageContextWithOptions((viewToBeRendered?.bounds.size)!, false, 0.0)
       viewToBeRendered!.drawViewHierarchyInRect(viewToBeRendered!.bounds, afterScreenUpdates: true)
       viewToBeRendered!.layer.renderInContext(UIGraphicsGetCurrentContext()!)

       let finalImage = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()

       return finalImage
   }

}

용법:

override func viewDidLoad() {
    super.viewDidLoad()

    //Sample View To Self.view
    let sampleView = UIView(frame: CGRectMake(100,100,200,200))
    sampleView.backgroundColor =  UIColor(patternImage: UIImage(named: "ic_120x120")!)
    self.view.addSubview(sampleView)    

    //ImageView With Image
    let sampleImageView = UIImageView(frame: CGRectMake(100,400,200,200))

    //sampleView is rendered to sampleImage
    var sampleImage = UIImage.renderUIViewToImage(sampleView)

    sampleImageView.image = sampleImage
    self.view.addSubview(sampleImageView)

 }

3

스위프트 3.0 구현

extension UIView {
    func getSnapshotImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
        drawHierarchy(in: bounds, afterScreenUpdates: false)
        let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return snapshotImage
    }
}

3

모든 Swift 3 답변이 효과가 없었으므로 가장 많이 받아 들여진 답변을 번역했습니다.

extension UIImage {
    class func imageWithView(view: UIView) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }
}

2

다음 은 @Dima의 답변을 기반으로 한 Swift 4 UIView 확장 입니다.

extension UIView {
   func snapshotImage() -> UIImage? {
       UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
       drawHierarchy(in: bounds, afterScreenUpdates: false)
       let image = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()
       return image
   }
}

2

Swift 5.1의 경우 다음 확장을 사용할 수 있습니다.

extension UIView {

    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)

        return renderer.image {
            rendererContext in

            layer.render(in: rendererContext.cgContext)
        }
    }
}

1

UIGraphicsImageRendereriOS 10에 도입 된 비교적 새로운 API 입니다. 포인트 크기를 지정하여 UIGraphicsImageRenderer 생성합니다 . 이미지 메소드는 클로저 인수를 사용하여 전달 된 클로저를 실행 한 결과 비트 맵을 리턴합니다. 이 경우 결과는 지정된 범위 내에서 그리도록 축소 된 원본 이미지입니다.

https://nshipster.com/image-resizing/

따라서 전달하는 크기가 UIGraphicsImageRenderer픽셀이 아닌 점 인지 확인하십시오 .

이미지가 예상보다 큰 경우 크기를 배율로 나눠야합니다.


궁금합니다. 제발 도와주세요. stackoverflow.com/questions/61247577/… UIGraphicsImageRenderer를 사용하고 있는데, 문제는 내 보낸 이미지가 장치의 실제보기보다 더 큰 크기가되기를 원한다는 것입니다. 그러나 원하는 크기의 하위 뷰 (라벨)로는 충분히 선명하지 않으므로 품질이 떨어집니다.
Ondřej Korol


0
- (UIImage*)screenshotForView:(UIView *)view
{
    UIGraphicsBeginImageContext(view.bounds.size);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // hack, helps w/ our colors when blurring
    NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg
    image = [UIImage imageWithData:imageData];

    return image;
}

-2

이 방법에서는 뷰 객체를 전달하면 UIImage 객체가 반환됩니다.

-(UIImage*)getUIImageFromView:(UIView*)yourView
{
 UIGraphicsBeginImageContext(yourView.bounds.size);
 [yourView.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 return image;
}

-6

UIView 카테고리에 메소드에 추가

- (UIImage*) capture {
    UIGraphicsBeginImageContext(self.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}

2
히야, 이것이 질문에 잘 대답 할 수는 있지만 다른 사용자는 당신만큼 지식이 없을 수도 있습니다. 왜이 코드가 작동하는지에 대한 작은 설명을 추가하지 않겠습니까? 감사!
Vogel612
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.