Jiseob Kim

iOS Developer

Swift - setNeedsLayout() VS layoutIfNeeded() (feat. animation)

09 Jul 2019 » Swift

์ฐธ๊ณ :

  1. zedd๋‹˜ ๋ธ”๋กœ๊ทธ
  2. ์–ด๋Š ์‚ฌ์ดํŠธ

๋ง‰์—ฐํ•˜๊ฒŒ layoutIfNeeded() ๋ฅผ ์จ์™”์—ˆ๋Š”๋ฐ, ๋ณผ๋•Œ๋งˆ๋‹ค ์œ ์‚ฌํ•ด ๋ณด์ด๋Š” setNeedsLayout()์€ ๋ญ˜๊นŒ ์‹ถ์—ˆ๋‹ค,

updateConstraints()๋„ ๊ถ๊ธˆํ•˜๊ณ , ๋น„์Šทํ•œ ๋‹ค๋ฅธ๊ฒƒ๋“ค๋„ ๊ถ๊ธˆํ•˜์ง€๋งŒ, ์ž๋ฃŒ์ข€ ์ฐพ์•„๋ณด๋‹ˆ ์ €๋‘˜์ด ๋น„๊ตํ•˜๋Š”๊ฒŒ ๋งŽ์•˜๊ธฐ์— ์ •๋ฆฌ!

Zedd๋‹˜ ๋ธ”๋กœ๊ทธ๊ฐ€ ๋„ˆ๋ฌด ์ •๋ฆฌ๊ฐ€ ์ž˜๋˜์–ด์žˆ์–ด์„œ ๊ธฐ๋ณธ์ ์ธ๊ฑด ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ , ์ดํ•ด๋ ฅ์ด ๋”ธ๋ ค ์‚ด์ง ๊ฐธ์šฐ๋šฑ ํ–ˆ๋˜ ๋ถ€๋ถ„๋“ค์„ ์ ์–ด์•ผ๊ฒ ๋‹ค



๋“ค์–ด๊ฐ€๊ธฐ์ „ ๋จผ์ € ์•Œ์•„์•ผ ์ดํ•ด๊ฐ€ ๋˜๋Š”๊ฒƒ!


Life cycle์ค‘ view layout์„ update ํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค! ๊ธ€๋“ค์„ ๋ณด๋‹ˆ layoutSubviews()๊ฐ€ ๊ทธ ์—ญํ• ์ธ๊ฐ€๋ณด๋‹ค. (Zedd๋‹˜ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค)

๊ทผ๋ฐ ๋ง ๊ทธ๋Œ€๋กœ, Cycle์ด๋‹ค, ํ•œ๋ฒˆ ์ง€๋‚˜๊ฐ€๋ฉด ๋‚˜์ค‘์— ์˜ค๋Š” ๊ทธ๋Ÿฐ๊ฒƒ! ์š”๊ฒŒ ํฌ์ธํŠธ๋‹ค.

๋˜ํ•œ ํ•ด๋‹น ์‹ธ์ดํด์ด ์™”์–ด๋„ ๊ทธ view์˜ layout์ด ๋ฌดํšจํ™” ๋˜์žˆ์–ด์•ผ ์ƒˆ๋กœ ์งœ๋Š”๊ฒƒ ๊ฐ™๋‹ค

๊ทธ๋ž˜์„œ, Constraint๋ฅผ ์ˆ˜์ •ํ•ด๋„ ๋ฐ”๋กœ ์ ์šฉ์ด ์•ˆ๋˜๊ณ ,

SetNeedsLayout() ๋˜๋Š” LayoutIfNeeded() ๋ฅผ ํ•ด์„œ ์ ์šฉ์„ ํ•ด์ค˜์•ผ ํ–ˆ๋‚˜๋ณด๋‹ค.



setNeedsLayout()

Point

  • Async
  • ๋Œ€๊ธฐ


์œ„์—์„œ ๋ฌดํšจํ™”๊ฐ€ ๋ผ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ ์—ˆ๋Š”๋ฐ, ๋ฌดํšจํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ setNeedsLayout()์„ ์จ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

โ€œํ•ด๋‹น view ๋‹ค์Œ ์‚ฌ์ดํด๋•Œ ์—…๋ฐ์ดํŠธ ํ•ด์ค˜โ€ ๋ผ๋Š” ์˜๋ฏธ๋ฉฐ, ์ฐธ๊ณ  2๋ฒˆ ์‚ฌ์ดํŠธ์— ์˜ํ•˜๋ฉด, ๊ทธ subviews๋„ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋œ๋‹ค.

๋ฐ”๋กœ ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋ฉฐ ๋Œ€๊ธฐ ์ƒํƒœ์ด๊ณ , ๋น„๋™๊ธฐ๋กœ ์ง„ํ–‰๋œ๋‹ค, ๊ทธ๋ฆฌ๊ณ  ์ด cycle์€ ์–ธ์ œ์˜ค๋Š”์ง€ ์•Œ๊ธฐ ํž˜๋“ค๋‹ค๊ณ  ํ•œ๋‹ค.

โ€“2๋ฒˆ ์‚ฌ์ดํŠธ ํ‘œํ˜„โ€“
โ€œplease update but you can wait until the next update cycleโ€
์—…๋ฐ์ดํŠธ ํ•ด์ค˜, ๊ทผ๋ฐ ๋‹ค์Œ ์—…๋ฐ์ดํŠธ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด๊ฒŒ



layoutIfNeeded()

point

  • Sync
  • ์ฆ‰์‹œ


์ฐจ์ด์ ์ด๋ผ๋ฉด ๋™๊ธฐ๋กœ ์ง„ํ–‰๋˜๊ณ , ๋‹ค์Œ cycle์ด ๋ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์—…๋ฐ์ดํŠธ ์‹œํ‚ฌ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋‹ค

โ€“2๋ฒˆ ์‚ฌ์ดํŠธ ํ‘œํ˜„โ€“
โ€œupdate immediately pleaseโ€
์—…๋ฐ์ดํŠธ ํ•ด์ค˜, ๋‹น์žฅ!



์˜ˆ์‹œ๋ฅผ ํ†ตํ•œ ์ดํ•ด

Zedd๋‹˜๊ป˜์„œ ์ด ๋งํฌ๋ฅผ ์ถ”์ฒœํ•ด์ฃผ์…จ๋‹ค.

  • setNeedsLayout()์€ ๋‹ค์Œ ์—…๋ฐ์ดํŠธ๋•Œ ๊ธฐ๋‹ค๋ฆด๊ฒŒ!
  • layoutIfNeeded()์€ ์ง€๊ธˆ ๋‹น์žฅ ์—…๋ฐ์ดํŠธ ํ•ด์ค˜!


๊ฒฐ๋ก ์€ โ€œ๋‘˜๋‹ค ์ƒˆ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ ํ•ด์ค˜!โ€ ๋กœ ์ดํ•ด๋ฅผ ํ–ˆ๋‹ค. ๊ทผ๋ฐ ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋‹ˆ ์ž˜ ์ดํ•ด๊ฐ€ ์•ˆ๋๋‹ค,

self.blueHeight.constant = 100

UIView.animate(withDuration: 2.0, animations: {
    self.view.setNeedsLayout()
    // ๋˜๋Š”
    self.view.layoutIfNeeded()
})


์˜ˆ์ œ์—์„œ ์ด๊ฒŒ ์ดํ•ด๊ฐ€ ์•ˆ๋œ๋‹ค. layoutIfNeeded()๋Š” ๋†’์ด๊ฐ’ constant๊ฐ€ ๋ณ€๊ฒฝ ๋˜์—ˆ์„๋•Œ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๊ฐ€ ๋ณด์ด๋ฉด์„œ height๊ฐ’์ด ๋ฐ”๋€๋‹ค.

๋ฐ˜๋ฉด, setNeedsLayout()์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ์—†์ด ๋ฐ”๋กœ ์ƒˆ๋กœ์šด height๊ฐ’์ด ์ ์šฉ๋œ๋‹ค.


๊ธฐ์กด๊ฐ’์€ 0, ์ƒˆ๊ฐ’์„ 100์ด๋ผ ํ•ด๋ณด์ž, ๋‘˜๋‹ค constant๊ฐ€ 100์œผ๋กœ ๋๋‹ค๋Š” ์˜๋ฏธ๋‹ค.

๊ทธ๋Ÿผ ์œ„์— ์„œ์ˆ ํ•œ ๋‚ด์šฉ๋“ค์„ ๋ถ™์—ฌ๋ณด์ž

  • setNeedsLayout() : 100์œผ๋กœ ๋ฐ”๊ฟจ์–ด, ๋‹ค์Œ ์—…๋ฐ์ดํŠธ๋•Œ ์ ์šฉํ•ด์ค˜
  • layoutIfNeeded(): 100์œผ๋กœ ๋ฐ”๊ฟจ์–ด, ๋‹น์žฅ ๋ฐ”๊ฟ”์ค˜

์ด๋ ‡๊ฒŒ ์ƒ๊ฐ์„ ํ•ด์„œ ์ดํ•ด๊ฐ€ ์•ˆ๋๋‹ค.


์™œ ์ดํ•ด๊ฐ€ ์•ˆ๋๋Š”์ง€ ์ ์–ด๋ณด๋ฉด,

  1. ๊ฒฐ๊ตญ์—” ์ด๋ฏธ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ˜ธ์ถœ์ „ 100์œผ๋กœ ์„ ์–ธํ•ด์คฌ์œผ๋‹ˆ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ํ• ๋•Œ๋ฉด, ์ด๋ฏธ 100์•„๋‹Œ๊ฐ€?? ๊ทธ๋Ÿผ cycle๋Œ๋‹ค๊ฐ€ ์—…๋ฐ์ดํŠธํ• ๋•Œ ์ด๋ฏธ 100์ธ๊ฑฐ ๊ฐ™์•„! ๊ทผ๋ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฃผ๋ฉด layoutIfNeeded()๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค? ๊ทธ๋Ÿผ ์• ๋‹ˆ๋ฉ”์ด์…˜๋™์•ˆ cycle์ด ์ ์šฉ ์•ˆ๋œ๊ฑด๊ฐ€? ๊ทธ๋Ÿผ setNeedsLayout()์€ 2์ดˆ ๋’ค์— ๊ฐ’์ด ๋ฐ”๋€Œ์–ด์•ผํ•˜๋Š”๊ฒƒ ์•„๋‹Œ๊ฐ€?!

์ด์ƒํ•œ์ : setNeedsLayout()์ด 2์ดˆ๋’ค๊ฐ€ ์•„๋‹Œ ์‹œ์ž‘ํ•˜์ž๋งˆ์ž ๊ฐ’์ด ๋ฐ”๋€œ??? ์ž‰ ์•„์˜ˆ ํ‹€๋ ค๋ฒ„๋ฆผ

  1. 1๋ฒˆ์ด ์ด์ƒํ–ˆ๋‹ค, ์‹œ์ž‘๊ณผ ๋™์‹œ์— cycle์ด ๋ˆ๊ฑฐ๋ผ๋ฉด height๋Š” 100์œผ๋กœ ์ด๋ฏธ ๋ฐ•ํ˜€์žˆ๋Š”๊ฑฐ๊ณ  ์ ์šฉ์ด ๋œ๊ฑฐ๋‹ค? ๊ทผ๋ฐ! ์ด๋ฏธ 100์ด๋ฉด layoutIfNeeded() ๋„ cycle ์‹œ์ž‘์‹œ ์ด๋ฏธ 100์ด๋‹ˆ๊น, ์ ์  100์ด ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ์ด๋ฏธ 100์ด๊ฒ ๋„ค?

์ด์ƒํ•œ์ : ์™œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋œ๊ฑฐ์ง€? ๋ฐ”๋กœ 100์œผ๋กœ ๋˜์•ผํ•˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€?!


๋ฐ”๋ณด์˜€๋‹ค. ๊ธ€์“ธ๋•Œ๊นŒ์ง€๋„ ์ž˜ ๋ชป ์ดํ•ดํ•˜๊ณ  ์žˆ์—ˆ๋‹คโ€ฆ



์›์ธ ํŒŒ์•…

Point_A

  • update cycle์€ ๋Œ๊ณ  ๋ˆ๋‹ค, ์œ ์ €๋Š” ์•Œ ์ˆ˜ ์—†๋‹ค

๊ทธ๋ ‡๋‹ค, ๋‚˜๋Š” ์•Œ ์ˆ˜ ์—†์—ˆ๋‹คโ€ฆ

์ด๊ฑธ ๋นผ๋จน๊ณ  ์ƒ๊ฐํ•˜๋‹ˆ ์ด์ƒํ•œ ์ถ”๋ก ๋งŒ ํ–ˆ๋‹ค.

์ค‘์ ์€ ์ด๊ฑฐ๋‹ค.


Point_B

  • animations ๋ธ”๋ก ์•ˆ์— ๋‚ด์šฉ์ด animation ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋œ๋‹ค,


Point_C

  • setNeedsLayout()๋Š” ๋‹ค์Œ update cycle๋•Œ.
  • layoutIfNeeded()๋Š” ์ฆ‰์‹œ.


ํฌ์ธํŠธ B์™€ C๋ฅผ ์„ž์–ด์„œ ๋ณด์ž

  • animation ๋ธ”๋Ÿญ ๋‚ด์—์„œ setNeedsLayout์€ ๋‹ค์Œ๋ฒˆ์— update cycle ๋•Œ ํ•ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•œ๋‹ค. ์ฆ‰, ์ง€๊ธˆ ํ•œ๊ฒƒ์€ ์š”์ฒญ๋งŒํ•จ.
  • animation ๋ธ”๋Ÿญ ๋‚ด์—์„œ layoutIfNeeded()๋Š” ์ฆ‰์‹œ update๋ฅผ ์š”์ฒญํ•œ๋‹ค, ๋ธ”๋Ÿญ ๋‚ด์—์„œ! ์ฆ‰, ์ง€๊ธˆ ์š”์ฒญ๊ณผ ๋™์‹œ์— ๋ณ€ํ™”๊ฐ€ ์‹œ์ž‘๋œ๋‹ค.


ํฌ์ธํŠธ๋Š” ๋ธ”๋Ÿญ๋‚ด์—์„œ ์š”์ฒญ๋งŒ ํ–ˆ๋Š๋ƒ, ์š”์ฒญ + ์ ์šฉ์ด ๋˜์—ˆ๋Š๋ƒ ์˜€๋‹ค.


ํ˜„์žฌ height = 0 ์ด์—ˆ๊ณ  ์‹ ๊ทœ๊ฐ’ height = 100์€ ์š”์ฒญ ์‚ฌํ•ญ์ด์—ˆ๋‹ค. layoutIfNeeded()๋Š” ๊ทธ ๊ฐ’์ด animation๋•Œ ์ ์šฉํ–ˆ๊ธฐ์— 0~100์ด ๋˜๋Š” ๊ณผ์ •์ด animation์œผ๋กœ ๋ณด์˜€๋‹ค.

๊ทธ๋Ÿฐ๋ฐ, setNeedsLayout()์˜ ๊ฒฝ์šฐ animations ๋ธ”๋Ÿญ์—์„  ์š”์ฒญ๋งŒ ํ–ˆ๊ธฐ์— ๋ณ„๋‹ค๋ฅธ UI์ ์œผ๋กœ ๋ณ€๊ฒฝ ์š”์†Œ๊ฐ€ ์—†์—ˆ๋‹ค.


๊ทธ๋ ‡๋‹ค๋ฉด, ํ•œ๋ฒˆ์— ๋ฐ”๋€๊ฑด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ• ๊นŒ? Point_A๊ฐ€ ํ•ด๋‹ต์ด๋‹ค.

Animations ๋ธ”๋Ÿญ์ด ์ง€๋‚œํ›„ cycle์ด ๋Œ๋‹ค๊ฐ€ update cycle์ด ๋œ๊ฒƒ์ด๋ผ ๋ณธ๋‹ค.

๊ทธ๋ ‡๊ธฐ์— ํ•ด๋‹น์‚ฌ์ดํด์€ setNeedsLayout()์ด ์š”์ฒญํ•œ height = 100์„ ์ ์šฉํ•œ๊ฒƒ์ด๋ฉฐ,

์ด๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๊ด€๋ จ์ด ์—†๊ธฐ์— ํ•œ๋ฒˆ์— ์ ์šฉ๋œ๊ฒƒ์ด๋‹ค.

์‚ฌ๋žŒ ๋ˆˆ์ด ์ฐฉ๊ฐํ•œ ๋Š๋‚Œ์ด๋ž„๊นŒ? ์‹œ๊ฐ์ ์œผ๋ก  height = 100์ด ์ ์šฉ๋˜๊ณ  ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์‹œ์ž‘๋œ๊ฒƒ ๊ฐ™์ง€๋งŒ

  1. animations ๋ธ”๋Ÿญ์—์„œ update ์š”์ฒญ
  2. ์ดํ›„ update cycle ์ ์šฉ & animation ์ง„ํ–‰์ค‘โ€ฆ
  3. animation ์ง„ํ–‰์ค‘ โ€ฆ ๋

์ด๋ ‡๊ฒŒ ๋˜๋Š”๊ฒŒ ๋งž๋‹ค๊ณ  ๋ณธ๋‹ค.


cycle์„ ๋‚ด๊ฐ€ ์ปจํŠธ๋กคํ•˜์ง€ ์•Š์•„์„œ ์ƒ๊ฐ์„ ๋ชปํ–ˆ๋‹ค. ์™„์ „ ๋ฐ”๋ณด์˜€๋‹คโ€ฆ.