๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS/iOS

[iOS] ์ด๋ฏธ์ง€์— ์›ํ•˜๋Š” ๋ชจ์–‘์˜ ๋งˆ์Šคํฌ ์ ์šฉํ•˜๊ธฐ

์•ˆ๋…•ํ•˜์„ธ์š” ์ œ์ธ์ž…๋‹ˆ๋‹ค~!

UI ๊ตฌํ˜„์— ๊ด€ํ•œ ๊ฒŒ์‹œ๊ธ€์€ ์ฒ˜์Œ ์—…๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ์š”,

๋ณต์žกํ•œ UI๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๊ฒช์—ˆ๋˜ ๋ฌธ์ œ๋“ค ํ˜น์€ ๊ณ ๋ฏผํ–ˆ๋˜ ์ ์„ ๋ธ”๋กœ๊ทธ์—๋„ ๊ธฐ๋ก์œผ๋กœ ๋‚จ๊ฒจ๋†“์•˜์œผ๋ฉด ์ข‹์•˜์„ ๊ฑธ,, ํ•˜๋Š” ์ƒ๊ฐ์—

์˜ˆ์ „ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฝค๋‚˜ ๊ณ ๋ฏผํ–ˆ๋˜ ๊ณผ์ •์„ ํฌ์ŠคํŒ…ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค..! (๊ณต์œ ํ•˜๋ฉด ์ข‹์œผ๋‹ˆ๊นŒ์š”..ใ…Ž)

์•ž์œผ๋กœ๋„ ์ด์Šˆ ํ•ด๊ฒฐ์— ๋Œ€ํ•œ ๋‚ด์šฉ๋„ ํฌ์ŠคํŒ…์„ ํ•ด๋ณด๋„๋ก ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

๋ณต์žกํ•œ ๋ชจ์–‘์˜ ๋งˆ์Šคํฌ๋ฅผ ์ด๋ฏธ์ง€์— ์ ์šฉํ•˜๊ธฐ

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด๋ฏธ์ง€์— ์‚ฌ๊ฐํ˜•, ์›์ฒ˜๋Ÿผ ๊ฐ„๋‹จํ•œ ๋„ํ˜•์ด ์•„๋‹ˆ๋ผ ๋ณต์žกํ•œ ๋ชจ์–‘์˜ ๋งˆ์Šคํฌ๋ฅผ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณต์œ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!

์˜ˆ์ „์— ์ฐธ์—ฌํ–ˆ์—ˆ๋˜ ํ”„๋กœ์ ํŠธ์—์„œ ์„œ๋น„์Šค์˜ ์‹ฌ๋ณผ์ธ ๋งˆ์‹œ๋ฉœ๋กœ ๋ชจ์–‘์ด ์—ฌ๋Ÿฌ ํ™”๋ฉด์—์„œ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ, ๋งˆ์‹œ๋ฉœ๋กœ ๋ชจ์–‘์˜ ๋งˆ์Šคํฌ๊ฐ€ ์ ์šฉ๋œ ์ด๋ฏธ์ง€๊ฐ€ ํ”„๋กœํ•„ ์‚ฌ์ง„์œผ๋กœ ์ ์šฉ๋˜์–ด ์•ฑ ๊ณณ๊ณณ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 


์›๋„ ์•„๋‹ˆ๊ณ .. ์‚ฌ๊ฐํ˜•๋„ ์•„๋‹ˆ๊ณ .. ์„ธ๋ชจ๋„ ์•„๋‹ˆ๊ณ .. ๋งˆ์‹œ๋ฉœ๋กœ ๋ชจ์–‘์„ ๋Œ€์ฒด ์–ด๋–ป๊ฒŒ ๋งˆ์Šคํฌ๋กœ ์ ์šฉํ•˜๋ƒ!!!

๊ธฐํš, ๋””์ž์ธ ํŒ€์›๋“ค์—๊ฒŒ ์ตœ๋Œ€ํ•œ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ๋‹ค๊ณ  ์ „๋‹ฌํ•˜๊ณ  ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ณ ๋ฏผ ๊ณผ์ •

๊ฐ€์žฅ ๋จผ์ € ๋– ์˜ค๋ฅธ ๋ฐฉ๋ฒ•์€ CALayer์˜ mask ํ”„๋กœํผํ‹ฐ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ณต์‹๋ฌธ์„œ์˜ ์„ค๋ช…์— ๋”ฐ๋ฅด๋ฉด,

CALayer์˜ mask๋Š” ์˜ต์…”๋„ ๋ ˆ์ด์–ด๋กœ ๋ ˆ์ด์–ด์˜ ์ปจํ…์ธ ์— ๋งˆ์Šคํฌ๋ฅผ ์”Œ์šฐ๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์•ŒํŒŒ ์ฑ„๋„์ž…๋‹ˆ๋‹ค.
๋ ˆ์ด์–ด์˜ ์•ŒํŒŒ ์ฑ„๋„์€ ๋ ˆ์ด์–ด์˜ ์ฝ˜ํ…์ธ ์™€ ๋ฐฐ๊ฒฝ ์ค‘ ์–ด๋Š ์ •๋„๋ฅผ ๋ณด์—ฌ์ค„์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
์ „์ฒด ๋˜๋Š” ๋ถ€๋ถ„์ ์œผ๋กœ ๋ถˆํˆฌ๋ช…ํ•œ ํ”ฝ์…€์€ ๊ธฐ๋ณธ ์ฝ˜ํ…์ธ ๋ฅผ ๋ณด์—ฌ์ฃผ์ง€๋งŒ, ์™„์ „ํžˆ ํˆฌ๋ช…ํ•œ ํ”ฝ์…€์€ ํ•ด๋‹น ์ฝ˜ํ…์ธ ๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

 

์–ด๋–ค ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ  ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ, ๊ทธ ๋ถ€๋ถ„์„ ํˆฌ๋ช…์ฒ˜๋ฆฌํ•ด์„œ ๊ฐ€๋ฆฌ๊ณ  ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Layer ๊ณ„์ธต ์ •๋„๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ maskedImage๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์ด์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค!

 

๊ทธ๋Ÿฐ๋ฐ, ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ฐพ์•„๋ณด๋‹ˆ CALayer์˜ mask์— CAShapeLayer ์ธ์Šคํ„ด์Šค๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ๋งˆ์Šคํ‚น์„ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ๊ณ ,

์ด ๋•Œ, CAShapeLayer์˜ path์— UIBezierPath๋ฅผ ํ™œ์šฉํ•ด ์ง์ ‘ ์›ํ•˜๋Š” ๋ชจ์–‘์˜ path๋ฅผ ๊ทธ๋ ค์„œ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.. (๋งˆ์‹œ๋ฉœ๋กœ ๋ชจ์–‘์„ ์–ด๋–ป๊ฒŒ ๋˜‘๊ฐ™์ด ๊ทธ๋ ค์š”..๐Ÿฅฒ)

 

ํ•˜์ง€๋งŒ, mask์— ๋Œ€ํ•ด ์ฐพ์•„๋ณด๋ฉด์„œ UIView์—๋„ mask๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๊ณต์‹ ๋ฌธ์„œ์˜ ์„ค๋ช…์„ ๋ณด๋ฉด, CALayer์˜ mask์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๋Š”๋ฐ, ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ๋Š” ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹ˆ๋ผ ๋ทฐ์ด๋‹ค! ๋ผ๋Š” ์ฐจ์ด๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ทฐ์˜ ์ฝ˜ํ…์ธ ๋ฅผ ๋งˆ์Šคํ‚นํ•˜๋Š” ๋ฐ ์•ŒํŒŒ ์ฑ„๋„์„ ์‚ฌ์šฉํ•˜๋Š” ์„ ํƒ์  ๋ทฐ ์ •๋„๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ,,

๊ทธ๋ ‡๋‹ค๋ฉด UIImageView ๋˜ํ•œ UIView๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ์ด mask ํ”„๋กœํผํ‹ฐ์— ์›ํ•˜๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋„ฃ์–ด ๋งˆ์Šคํ‚น์„ ํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ..?

ํ•˜๋Š” ์•„์ด๋””์–ด๊ฐ€ ๋– ์˜ฌ๋ž์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์•„์ด๋””์–ด๋กœ ๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ๋” ์ฐพ์•„ ์›ํ•˜๋Š” maskedImage๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๋ฐฉ๋ฒ•

maskedImage Custom Class

class PomeMaskedImageView: UIImageView {
    
    // MARK: Properties
    private var maskImageView = UIImageView()
    
    var maskImage: UIImage? {
        didSet {
            maskImageView.image = maskImage
            updateImageView()
        }
    }
    
    // MARK: Life Cycle
    override func layoutSubviews() {
        super.layoutSubviews()
        updateImageView()
    }
    
    // MARK: Custom Methods
    
    /// ์›๋ณธ ์ด๋ฏธ์ง€์— mask๋ฅผ ์ ์šฉํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ
    private func updateImageView() {
        guard let image = maskImageView.image else {
            return
        }
        maskImageView.frame = bounds
        mask = maskImageView
    }
}

 

๋งˆ์Šคํฌ๊ฐ€ ์”Œ์›Œ์ง„ ์ด๋ฏธ์ง€๊ฐ€ ํ™”๋ฉด ๊ณณ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด Custom Class๋กœ ์ •์˜ํ•ด๋‘๊ณ  ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์— ๋งˆ์Šคํฌ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

 

<ํ”„๋กœํผํ‹ฐ>

  • maskImageView: ๋งˆ์Šคํฌ๋กœ ์‚ฌ์šฉ๋  ์ด๋ฏธ์ง€๋ทฐ
  • maskImage: ๋งˆ์Šคํฌ๋กœ ์‚ฌ์šฉ๋  ์ด๋ฏธ์ง€ (๋งˆ์‹œ๋ฉœ๋กœ ๋ชจ์–‘ ์ด๋ฏธ์ง€) - ์™ธ๋ถ€์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ง€์ •ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด public์œผ๋กœ ์„ ์–ธ

<๋ฉ”์„œ๋“œ>

updateImageView()

์›๋ณธ ์ด๋ฏธ์ง€์— mask๋ฅผ ์ ์šฉํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

๋งˆ์Šคํฌ๋กœ ์‚ฌ์šฉ๋  ์ด๋ฏธ์ง€(maskImageView.image)๊ฐ€ nil์ด ์•„๋‹ˆ๋ผ๋ฉด ,

maskImageView์˜ frame์„ PomeMaskedImageView์˜ bounds์— ๋งž์ถฅ๋‹ˆ๋‹ค. (๋งˆ์Šคํฌ๊ฐ€ ์›๋ณธ์ด๋ฏธ์ง€ ์œ„์— ์ •ํ™•ํžˆ ๊ฒน์ณ์ ธ ์›ํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ๋ณด์—ฌ์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ)

๊ทธ๋Ÿฐ ๋‹ค์Œ, PomeMaskedImageView์˜ mask ํ”„๋กœํผํ‹ฐ์— maskImageView๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

 

didSet ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„ ์‚ฌ์šฉ ์ด์œ ?

didSet ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„๋Š” maskImage์— ์ƒˆ๋กœ์šด ๊ฐ’์ด ์„ค์ •๋  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. (์™ธ๋ถ€์—์„œ ๋งˆ์Šคํฌ ์ด๋ฏธ์ง€์— ๊ฐ’์ด ํ• ๋‹น๋  ๋•Œ๋งˆ๋‹ค)

didSet ๋ธ”๋ก ์•ˆ์—์„œ๋Š” maskImageView์˜ image๋ฅผ ์ƒˆ๋กœ์šด maskImage๋กœ ์„ค์ •ํ•˜๊ณ , updateImageView() ๋ฅผ ํ˜ธ์ถœํ•ด ์ด๋ฏธ์ง€ ๋ทฐ์— ๋งˆ์Šคํฌ๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

layoutSubviews()์—์„œ updateImageView() ํ˜ธ์ถœ ์ด์œ ?

updateImageView() ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ ๋งˆ์Šคํฌ๊ฐ€ ์ •ํ™•์ด ์›๋ณธ ์ด๋ฏธ์ง€ ์œ„์— ๊ฒน์ณ์ง€๋„๋ก ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š”, ๋งˆ์Šคํ‚นํ•˜๋ ค๋Š” ๋ทฐ(์›๋ณธ ์ด๋ฏธ์ง€)์˜ bounds ๊ฐ’์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„ ๋™์•ˆ์— bounds ๊ฐ’์ด ์ƒˆ๋กœ ๊ณ„์‚ฐ๋˜๊ณ  ์ ์šฉ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” layoutSubviews()์—์„œ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ทฐ์˜ ๋ ˆ์ด์•„์›ƒ์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋งˆ์Šคํฌ ์ด๋ฏธ์ง€์˜ frame์„ ๊ฐฑ์‹ ํ•˜์—ฌ ํ•ญ์ƒ ์˜ฌ๋ฐ”๋ฅธ ํฌ๊ธฐ์™€ ์œ„์น˜์— ๋งˆ์Šคํฌ๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ) layoutSubviews() ๋ฉ”์„œ๋“œ๋Š” ๋ทฐ์˜ ๋ ˆ์ด์•„์›ƒ์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ ์ธ๋ฐ, ์ด ๋•Œ๋งˆ๋‹ค ๋ทฐ์˜ bounds์™€ frame์ด ์ƒˆ๋กœ ๊ณ„์‚ฐ๋˜๊ณ  ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•˜๋Š” ๊ณณ

private let profileImageView = PomeMaskedImageView().then {
    $0.image = UIImage(named: "userProfileEmpty160") // ์›๋ณธ ์ด๋ฏธ์ง€
    $0.maskImage = UIImage(named: "userProfileEmpty160") // ๋งˆ์Šคํฌ๋กœ ์‚ฌ์šฉํ•  ์ด๋ฏธ์ง€
}

 

์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ๋Š” ์œ„์™€ ๊ฐ™์ด ์›๋ณธ ์ด๋ฏธ์ง€์™€ ๋งˆ์Šคํฌ๋กœ ์‚ฌ์šฉํ•  ์ด๋ฏธ์ง€๋ฅผ ํ• ๋‹นํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค!

๊ฒฐ๊ณผ ํ™”๋ฉด

 


[์ฐธ๊ณ  ์ž๋ฃŒ]

https://stackoverflow.com/questions/5757386/how-can-i-mask-a-uiimageview

https://ios-development.tistory.com/940