'screenshot'에 해당되는 글 3건

반응형

네이티브 UIScrollView 화면에서 스크린샷 구하는 방법에 대해 알아보자.

일반적으로 스크린샷을 구해보면 화면에 보이는 부분만

구해지며 숨겨진 영역은 흰색화면으로 출력된다. (iOS 13.0이상 에서 생기는 버그)

extension UIScrollView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        let savedContentOffset = contentOffset
        let savedFrame = frame
        defer {
            UIGraphicsEndImageContext()
            contentOffset = savedContentOffset
            frame = savedFrame
        }
        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
    }
}

이를 위해 UIScrollView를 부모로 부터 Layout 컨텐츠를 비활성하고 스크린샷 이미지를 구하고 다시

원래대로 활성화 시켜주는 방법으로 해결 가능하다.

extension UIScrollView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        let savedContentOffset = contentOffset
        let actualConstraints = relatedConstraints()
        NSLayoutConstraint.deactivate(actualConstraints)
        translatesAutoresizingMaskIntoConstraints = true
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
        contentOffset = .zero
        defer {
            UIGraphicsEndImageContext()
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate(actualConstraints)
            superview?.layoutIfNeeded()
            contentOffset = savedContentOffset
        }
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: ctx)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        return image
    }
}


extension UIView {
    func relatedConstraints() -> [NSLayoutConstraint] {
        var constraints = self.constraints
        var parent = superview
        while parent != nil {
            constraints.append(contentsOf: parent!.constraints.filter { $0.firstItem === self || $0.secondItem === self })
            parent = parent!.superview
        }
        return constraints
    }
    
    class func getAllSubviews<T: UIView>(from parenView: UIView) -> [T] {
        return parenView.subviews.flatMap { subView -> [T] in
            var result = getAllSubviews(from: subView) as [T]
            if let view = subView as? T { result.append(view) }
            return result
        }
    }
	
    class func getAllSubviews(from parenView: UIView, types: [UIView.Type]) -> [UIView] {
        return parenView.subviews.flatMap { subView -> [UIView] in
            var result = getAllSubviews(from: subView) as [UIView]
            for type in types {
                if subView.classForCoder == type {
                    result.append(subView)
                    return result
                }
            }
            return result
        }
    }
	
    func getAllSubviews<T: UIView>() -> [T] { return UIView.getAllSubviews(from: self) as [T] }
    func get<T: UIView>(all type: T.Type) -> [T] { return UIView.getAllSubviews(from: self) as [T] }
    func get(all types: [UIView.Type]) -> [UIView] { return UIView.getAllSubviews(from: self, types: types) }
}


/*
// 현재 뷰의 하위뷰 중 UIScrollView를 구한다.
if let scrollView = self.view.get(all: UIScrollView.self).first {
    // 스크롤뷰의 모든 컨텐츠가 나오도록 스크린샷 이미지로 반환한다.
    let image = scrollView.screenshot()
    
    // 주어진 이미지를 공유
    DispatchQueue.main.async {
        let imageToShare = [ image ]
        let activityVC = UIActivityViewController(activityItems: imageToShare as [Any], applicationActivities: nil)
        self.present(activityVC, animated: true, completion: nil)
    }
}
*/

 

반응형
블로그 이미지

SKY STORY

,
반응형

다음은 스크린 캡쳐 이벤트 처리 방법이다.

말 그대로 이벤트만 받는다. 막는건 안된다.

// Objective C
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
                                                  object:nil
                                                   queue:mainQueue
                                              usingBlock:^(NSNotification *note) {
                                                 // executes after screenshot
                                              }];


// Swift
NotificationCenter.default.addObserver(
    forName: UIApplication.userDidTakeScreenshotNotification,
    object: nil,
    queue: .main) { notification in
        //executes after screenshot
}



방법 1
func detectScreenShot(action: @escaping () -> ()) {
    let mainQueue = OperationQueue.main
    NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
        // executes after screenshot
        action()
    }
}


방법  2
func detectScreenShot(action: () -> ()) {
    let mainQueue = NSOperationQueue.mainQueue()
    NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
        // executes after screenshot
        action()
    }
}

방법1, 2 실행방법 
detectScreenShot { () -> () in
    print("User took a screen shot")
}
반응형
블로그 이미지

SKY STORY

,

WKWebView 스크린샷

개발/iOS 2020. 5. 12. 15:34
반응형

WKWebView에서 화면 스크롤영역 전체 스크린샷을 구하는 방법을 알아보겠습니다.

iOS 11버전 부터 takeSnapshot이라는 함수를 제공하지만 실제로 정상적인 결과를 얻을 수 없습니다.

아래 클래스를 이용하면 보여지지 않는 스크롤 영역 전체의 스크린 샷을 구해 고화질로 저장할 수 있습니다.

주의 : 해당 함수는 웹페이지 로딩이 완료된 뒤 호출하셔야 합니다.

extension WKWebView{
    // 화면에 보여지는 부분만 스크린샷
    func screenshot(_ completionBlock: ((UIImage) -> Void)?) {
        if #available(iOS 11.0, *) {
            let config = WKSnapshotConfiguration()
            if #available(iOS 13.0, *) {
                config.afterScreenUpdates = false
            }
            config.rect = self.frame
            // iOS 11.0부터 takeSnapshot 함수를 사용할 수 있으나 스크롤영역 전체 스크린샷은 지원하지 않음(시뮬레이터는 가능)
            self.takeSnapshot(with: config, completionHandler: { image, error in
                if error != nil {
                    print("\(String(describing: error))")
                    return
                }
                guard let image = image else { return }
                completionBlock?(image)
            })
        } else {
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, UIScreen.main.scale);
            self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
            guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return }
            UIGraphicsEndImageContext();
            completionBlock?(image)
        }
    }
    
    // 현재 페이지의 모든 컨텐츠 스크린샷
    func screenshot2(_ completionBlock: ((UIImage) -> Void)?) {
        let sv = self.scrollView
        let offset = sv.contentOffset
        let pageSize = sv.contentSize;
        let maxWidth = CGFloat(ceil(pageSize.width/sv.frame.size.width))
        let maxHeight = CGFloat(ceil(pageSize.height/sv.frame.size.height))
        for x in stride(from: 0, to: maxHeight, by: 1.0) {
            for y in stride(from: 0, to: maxWidth, by: 1.0) {
                sv.scrollRectToVisible(CGRect(x: sv.frame.size.width  * x,
                                              y: sv.frame.size.height * y,
                                              width:  sv.frame.size.width,
                                              height: sv.frame.size.height), animated: true)
                RunLoop.main.run(until: Date.init(timeIntervalSinceNow: 1.0))
            }
        }
        sv.setContentOffset(offset, animated: false)
        
        let prevOffset = sv.contentOffset
        let prevBounds = self.bounds
        let prevFrame = self.frame
        
        UIGraphicsBeginImageContext(pageSize)
        self.bounds = CGRect(x: self.bounds.origin.x, y: self.bounds.origin.y, width: pageSize.width, height: pageSize.height)
        self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: pageSize.width, height: pageSize.height)
        RunLoop.main.run(until: Date.init(timeIntervalSinceNow: 0.5))
        self.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        self.bounds = prevBounds
        self.frame = prevFrame
        sv.setContentOffset(prevOffset, animated: false)
        completionBlock?(image)
    }
}

 

☞ 웹뷰 화면 보여지는 부분만 스크린샷

webView.screenshot({(image) in
    print("complete")
})

 

☞ 웹뷰 화면 전체 스크린샷 (스크롤영역 전체)

webView.screenshot2({(image) in
    print("complete")
})

 

2020/05/12 - [iOS/Swift] - WKWebView 스크린샷

2020/05/12 - [iOS/Swift] - json 포멧 체크

2020/05/12 - [iOS/Swift] - Access Control (접근 제한자)

2020/05/12 - [iOS/Swift] - WKWebview에서 tel, email, mailto, sms, facetime 처리

 

반응형

'개발 > iOS' 카테고리의 다른 글

iOS디바이스 설정창 이동  (0) 2020.05.15
Xcode 한글 깨짐 복구  (0) 2020.05.15
json 포멧 체크  (0) 2020.05.12
Access Control (접근 제한자)  (0) 2020.05.12
WKWebview에서 tel, email, mailto, sms, facetime 처리  (0) 2020.05.12
블로그 이미지

SKY STORY

,