iOS

[ iOS ] ํ™”๋ฉด ์บก์ณํ•˜๊ธฐ - UIGraphicsRenderer

Forest Yun 2023. 12. 22. 20:43
728x90

 

๐Ÿ”ซ ๋ฌธ์ œ ๋ฐœ์ƒ

iOS 17๊ฐ€ ๋ฐœํ‘œ๋˜๋ฉฐ, ์•ฑ์Šคํ† ์–ด์— ๋ฐฐํฌํ•œ ์„œ๋น„์Šค์— ํ˜น์‹œ deprecated๋œ ํด๋ž˜์Šค๋‚˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์„๊นŒ ์‹ถ์–ด ์ฐพ์•„๋ดค๋‹ค. ์ •๋ง ์•ˆํƒ€๊น๊ฒŒ๋„ '์ด๋ฏธ์ง€ ๊ณต์œ ํ•˜๊ธฐ' ๊ธฐ๋Šฅ์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ UIGraphicsBeginImageContextWithOptions(_:_:_:)๊ฐ€ iOS 17.0 ๋ฒ„์ „๊นŒ์ง€๋งŒ ์ œ๊ณต๋œ๋‹ค๊ณ  ํ•œ๋‹ค... 

๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ๋ฒ„์ „ ์—…์„ ํ• ๊นŒ ๊ฑฑ์ •ํ•˜๋ฉฐ ๊ณต์‹ ๋ฌธ์„œ์—์„œ Graphic, Image, Render๋ฅผ ํ‚ค์›Œ๋“œ๋กœ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ํด๋ž˜์Šค ๋˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์•„๋ดค๋‹ค. ๋‹คํ–‰ํžˆ ๋ฉ€์ง€ ์•Š๊ฒŒ Drawing ์นดํ…Œ๊ณ ๋ฆฌ์—์„œ UIGraphicsRenderer ํด๋ž˜์Šค๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค! ์ด์ œ ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ iOS 17 ์ดํ›„๋ฅผ ๋Œ€์‘ํ•˜๋Š” ๋ฆฌํŒฉํ† ๋ง์„ ์‹œ์ž‘ํ•ด๋ณด์ž.

 

 

 

 

 

๐ŸŽจ UIGraphicsRenderer

UIGraphicsRenderer
An abstract base class for creating graphics renderers.
๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์ถ”์ƒ์ ์ธ ๊ธฐ๋ณธ ํด๋ž˜์Šค

 

์ด ํด๋ž˜์Šค๋Š” ์ถ”์ƒํ™”๋œ ๊ธฐ๋ณธ ํด๋ž˜์Šค๋กœ, ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์œผ๋ฉฐ subclassing์ด ํ•„์š”ํ•˜๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ image์™€ PDF๋ฅผ ์œ„ํ•œ ํ•˜์œ„ ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณต๋˜๋ฉฐ, ๋” ๋‹ค์–‘ํ•˜๊ณ  ํ•„์š”ํ•œ ์ƒ์„ธ ๊ธฐ๋Šฅ์„ ์œ„ํ•ด ์ง์ ‘ ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

UIGraphicsImageRendererUIGraphicsPDFRenderer

 

๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋Ÿฌ๋Š” Core Graphics Contexts์˜ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ ๊ด€๋ฆฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด Core Graphics Contexts๋Š” 2D Graphics์— ๋Œ€ํ•œ ๋“œ๋กœ์ž‰ ํ™˜๊ฒฝ๊ณผ ๋ฐฑ์—… ์ €์žฅ์†Œ(backing store)๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋Ÿฌ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉด, Core Graphics Contexts๋„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

 

 backing store: 

swap device๋กœ, ํ•˜๋“œ์›จ์–ด์— ์†ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ swap์ด๋ž€ ์‹œ์Šคํ…œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•  ๊ฒฝ์šฐ ํ•˜๋“œ์›จ์–ด์˜ ์ผ๋ถ€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ž‘์—…์„ ๋„์™€์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. ๋‹จ์–ด์˜ ํ•œ๊ธ€ ๋œป์ด '๊ตํ™˜'์ด๋“ฏ, ๋ฉ”์ธ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ง€๊ธˆ ๋‹น์žฅ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„๊ณผ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ swap-in / swap-out ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋•๋Š”๋‹ค.

backing storeswap

 

 

 

๐Ÿ‘ฉ๐Ÿป‍๐ŸŽจ UIGraphicsImageRenderer

UIGraphicsImageRenderer
A graphics renderer for creating Core Graphics-backed images.
Core Graphics ์ง€์› ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋Ÿฌ์ด๋‹ค.

 

UIGraphicsImageRenderer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ๊ฐ ์ƒ‰ ์š”์†Œ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋น„ํŠธ ์ˆ˜(์ƒ‰ ์‹ฌ๋„)์™€ ์ด๋ฏธ์ง€ ๊ทœ๋ชจ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ์‹ ๊ฒฝ ์“ธ ํ•„์š”์—†๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ Core Graphics Contexts๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋“œ๋กœ์ž‰ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ํฌ๊ธฐ์™€ ํฌ๋งท ์„ค์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ด๋ฏธ์ง€ ๋ Œ๋”๋Ÿฌ๋ฅผ ์ดˆ๊ธฐ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ ํ›„ ์ด ๋ Œ๋”๋Ÿฌ์˜ ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

 

์ด์ „์— ์‚ฌ์šฉํ–ˆ๋˜ UIGraphicsBeginImageContextWithOptions์˜ ๊ฒฝ์šฐ, ๋“œ๋กœ์ž‰ ํ™˜๊ฒฝ์„ ๋”ฐ๋กœ ๊ตฌ์„ฑํ–ˆ์–ด์•ผ ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ํ•จ์ˆ˜์—์„œ context ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฐ˜ํ™˜๋˜์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ € ์–ด๋”˜๊ฐ€์— ๋งŒ๋“ค์–ด์ง„ context๊ฐ€ ์Šคํƒ์œผ๋กœ ์Œ“์—ฌ ํ˜„์žฌ context๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ตฌ์กฐ์—ฌ์„œ ์ง๊ด€์ ์ธ ์‚ฌ์šฉ์ด ์–ด๋ ค์› ๋‹ค.

ํ•˜์ง€๋งŒ ์ด ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๊ทธ๋ž˜ํ”ฝ ์ด๋ฏธ์ง€ ๋ Œ๋”๋Ÿฌ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด ๋ Œ๋”๋Ÿฌ ์‚ฌ์šฉ์ด ์ง๊ด€์ ์ด์–ด์„œ ์ข‹์•˜๋‹ค.

 

 

 

 

 

๐Ÿž๏ธ ํ™”๋ฉด์„ ์ด๋ฏธ์ง€๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ

UIGraphicsImageRenderer์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ํฌ๊ธฐ์™€ Context ์ƒ์„ฑ ์‹œ ์‚ฌ์šฉํ•  ํฌ๋งท์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‚˜๋Š” ๊ธฐ๋ณธ ํฌ๋งท์„ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๋ผ ๋”ฐ๋กœ ํฌ๋งท ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ, UIGraphicsImageRendererFormat ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์›ํ•˜๋Š” ํฌ๋งท์„ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.

 // Render Image
let renderer = UIGraphicsImageRenderer(size: self.frame.size)

 

 

 

๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋Ÿฌ์—๋Š” ์„ธ ๊ฐ€์ง€ ํ˜•ํƒœ์˜ ์ถœ๋ ฅ์„ ์ œ๊ณตํ•œ๋‹ค. UIImage๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” image(actions:), jped๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” jpegData(withCompressionQuality:actions:), png๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” pngData(actions:)์ด ์žˆ๋‹ค.

๋‚˜๋Š” ๊ธฐ์กด์— UIImage๊ฐ€ ๋ฐ˜ํ™˜๋˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๊ธฐ์— ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๊ฐ’์„ ์œ ์ง€ํ•˜๊ณ ์ž image(actions:)๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

let image = renderer.image { context in
     // ์ฝ”๋“œ
}

 

 

 

๊ทธ ๋‹ค์Œ ๋ฌด์—‡์„ ๋ Œ๋”๋งํ•  ์ง€๋ฅผ ์ •ํ•ด์„œ ์ด context์— ๋„˜๊ฒจ ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๋‚˜๋Š” ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— view.layer์˜ ๋ฉ”์„œ๋“œ์ธ render(in:) ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

์—ฌ๊ธฐ์„œ renderer, context, view, UIImage(๊ฒฐ๊ณผ)๋ฅผ ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š๊ฒŒ ๊ตฌ๋ถ„ํ•˜์ž๋ฉด(๋‚ด๊ฐ€ ์ƒ๊ฐํ•œ ๋Š๋‚Œ) ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๐Ÿ‘ฉ๐Ÿป‍๐ŸŽจ(ํ™”๊ฐ€) Renderer / ๐Ÿชง(๋„ํ™”์ง€) Context / โ›ฐ๏ธ(๋Œ€์ƒ) View / ๐Ÿž๏ธ(์™„์„ฑ๋œ ๊ทธ๋ฆผ) UIImage

 let image = renderer.image { context in
        self.layer.render(in: context.cgContext) // View๋ฅผ Extension ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— self ์‚ฌ์šฉ
 }

 

 

 

 

 

 

๐Ÿ“ธ ScrollView ์ „์ฒด ์บก์ณํ•˜๊ธฐ

ScrollView์™€ ๊ด€๋ จํ•ด ๋ Œ๋”๋ง ํฌ๊ธฐ๋ฅผ ๊ตฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ด์ „ ๊ธ€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[ iOS ] ScrollView ํ™”๋ฉด ์บก์ณํ•˜๊ธฐ (Image Creation)

๋ฌธ์ œ ๋ฌด๋‹ค, ๊พธ์ฃผ๋‹ˆ ๋“ฑ ๋ช‡๋ช‡ ์•ฑ๋“ค์€ ํ™”๋ฉด์„ ์บก์ณํ•œ ๋“ฏ ์ด๋ฏธ์ง€ํ™”ํ•ด ์ €์žฅ ๋˜๋Š” ๊ณต์œ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์˜ค๋Š˜์€ ์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ธ๋ฐ, ํ™”๋ฉด ์บก์ณ์—์„œ ๋” ๋‚˜์•„๊ฐ€ ํ™”๋ฉด ๋ฐ– ์•„๋ž˜๋กœ ๋‚ด์šฉ์ด ๊ณ„์†

88yhtserof.tistory.com

 

 

 

 

 

 

๐Ÿงš๐Ÿป‍โ™€๏ธ ์ „์ฒด ์ฝ”๋“œ ํ™•์ธํ•˜๊ธฐ

import UIKit

extension UIView {
    
    /// Render a Image from view to get a full screen snap shot.
    /// Return UIImage, or nil.
    func snapShotFullScreen(scrollView: UIScrollView) -> UIImage? {
        let savedFrame = self.frame
        let savedScrollviewFrame = scrollView.frame
        let savedContentOffset = scrollView.contentOffset
        let scrollContentFrame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
        
        scrollView.contentOffset = .zero
        scrollView.frame = scrollContentFrame
        self.frame = scrollContentFrame
        
        // Render Image
        let renderer = UIGraphicsImageRenderer(size: self.frame.size)
        let image = renderer.image { context in
            self.layer.render(in: context.cgContext)
        }
        
        // Initialize the set value
        scrollView.contentOffset = savedContentOffset
        scrollView.frame = savedScrollviewFrame
        self.frame = savedFrame

        return image
    }
}

 

 

 

 

 

๐Ÿค” ๋ฆฌํŒฉํ† ๋งํ•˜๋ฉฐ ๋Š๋‚€ ์ 

๊ธฐ์กด์— ์‚ฌ์šฉํ–ˆ๋˜ UIGraphicsBeginImageContextWithOptions ํ•จ์ˆ˜๊ฐ€ ์˜ค๋ž˜๋˜๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ, ์—ด์‹ฌํžˆ ๊ณต๋ถ€ํ•˜๊ณ  ๋งŒ๋“  ๊ธฐ๋Šฅ์„ ๋ฒ„์ „ ์—…์˜ ์ด์œ ๋กœ ๋ฆฌํŒฉํ† ๋งํ•ด์•ผํ•˜๋‹ˆ ์•„์‰ฌ์›€์ด ์ปธ๋‹ค. ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ณ  ๋‹ค๋ฅธ ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„ํ•ด๋ณด๋‹ˆ ์™œ Apple์—์„œ deprecatedํ–ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ƒˆ๋กœ ์‚ฌ์šฉํ•œ ํด๋ž˜์Šค๋Š” ๊ณต๋ถ€๊ฐ€ ์‰ฌ์› ๊ณ , ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ๋Š๊ปด์ง€๋Š” ๋ถ€๋ถ„์ด ์—†์—ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๋ฆฌํŒฉํ† ๋ง์„ ํ•˜๋‹ˆ ์ฝ”๋“œ ๋” ์ง๊ด€์ ์ด๊ณ  ๊ฐ„๋‹จํ•ด์กŒ๋‹ค. ์ด๋ฒˆ์— ์‚ฌ์šฉํ•œ UIGraphicRenderer๋„ iOS 10 ๋ถ€ํ„ฐ ์‚ฌ์šฉ๋˜๋˜ ํด๋ž˜์Šค๋ผ ์‚ฌ์‹ค ์˜ค๋ž˜๋œ ํŽธ์ด์ง€๋งŒ, visionOS์—์„œ๋„ ์ง€์›๋˜๊ณ  ์‹ค์ œ ๊ตฌํ˜„์—๋„ ์–ด๋ ค์›€์ด ์—†์–ด์„œ ํ•œ๋™์•ˆ์€ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

 

 

 

728x90