Published on

๐ŸŽŠ WWDC23 - What's new in UIKit

Authors
  • avatar
    Name
    ์ด์ฐฝ์ค€
    Twitter

WWDC23 - Whatโ€™s new in UIKit

๋“œ๋””์–ด WWDC23์˜ ๋ง‰์ด ์—ด๋ ธ์Šต๋‹ˆ๋‹ค!

๋‹ค๋“ค VisionOS ์™€ Vision Pro ๋ณด์…จ๋‚˜์š”??

๋„ˆ๋ฌด๋„ˆ๋ฌด ๋†€๋ผ์›Œ์„œ ๋‹น์žฅ์ด๋ผ๋„ ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ์€ ๋งˆ์Œ์ด ์ปธ์ง€๋งŒ..

์Šค์œ ์™€ ARKit์„ ๋ชจ๋ฅธ๋‹ค๋ฉด ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์„ ๊ฒƒ ๊ฐ™๋”๊ตฐ์š”..! ๐Ÿ˜ญ

๊ทธ๋ž˜์„œ ์—ฌ์œ ๊ฐ€ ์ข€ ์ƒ๊ธด๋‹ค๋ฉด ์ฒœ์ฒœํžˆ ํ•˜๋‚˜์”ฉ ๊ณต๋ถ€ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!

์ผ๋‹จ์€ ํ•˜๋˜ UIKit์— ์ง‘์ค‘ํ•ด์•ผ์ฃ !

๊ทธ๋ž˜์„œ ์˜ฌํ•ด ๋ฅ๋ฅ๋””์”จ ๊ณต๋ถ€์˜ ์‹œ์ž‘์„ โ€œWhatโ€™s new in UIKitโ€ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

Architectural Enhancements

์šฐ์„  Architectural Enhancements ์ž…๋‹ˆ๋‹ค! (์ด๊ฑฐ ํ•œ๊ตญ์–ด๋กœ ๋ญ๋ผ๊ณ  ๋ฒˆ์—ญํ•ด์•ผํ•˜๋‚˜์š”..)

์ด 5๊ฐ€์ง€์˜ ํŒŒํŠธ๋กœ ๋‚˜๋ˆ„์–ด ์„ค๋ช…๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Xcode Previews

๋“œ๋””์–ด Preview๋ฅผ UIKit ์ˆœ์ •์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ๐Ÿ‘๐Ÿ‘

์›๋ž˜๋Š” UIKit์—์„œ ์•ฑ์˜ ํ”„๋ฆฌ๋ทฐ๋ฅผ ์•ฑ ๋นŒ๋“œ ์—†์ด ๋ณด๋ ค๋ฉด SwiftUI์˜ feature๋ฅผ ๋นŒ๋ ค์™€์„œ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์ฃ .. ๐Ÿ˜ญ

์ด์ œ๋Š” import SwiftUI ์—†์ด ์ˆœ์ˆ˜ UIKit์—์„œ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!

๋ฐ”๋กœ ์ƒˆ๋กญ๊ฒŒ ๋“ฑ์žฅํ•œ Swift์˜ macro ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ์š”..!

์˜ค์˜คโ€ฆ #Preview๊นŒ์ง€๋งŒ ์ž…๋ ฅ์„ ํ–ˆ๋Š”๋ฐ๋„ ์˜†์— Preview ํ™”๋ฉด์ด ๋ฐ”๋กœ ๋œจ๋„ค์š”โ€ฆ ๐Ÿ˜ฎ

#Preview("Home") {
  let vc = HomeViewController()
  vc.isBold = false
  return vc
}

์ด๋ ‡๊ฒŒ ์ƒˆ๋กœ์šด macro๋ฅผ ์‚ฌ์šฉํ•ด์„œ SwiftUI์™€ ๊ฐ™์ด Preview๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

vc.type = .edit๊ณผ ๊ฐ™์ด vc์˜ ํ”„๋กœํผํ‹ฐ๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”๊ฟ”๊ฐ€๋ฉด์„œ ์ž‘์—…ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”!

์ด๋ ‡๊ฒŒ์š”..!

๊ทผ๋ฐ์š”.. ๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค.

iOS 17๋ถ€ํ„ฐ๊ฑฐ๋“ ์š”! ๐Ÿฅฒ ๐Ÿฅฒ ๐Ÿฅฒ

์•„๋ฌด๋ž˜๋„ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ์—” ์‹œ๊ฐ„์ด ์กฐ๊ธˆ ๊ฑธ๋ฆด ๊ฒƒ ๊ฐ™์ฃ ..?

์ €๋Š” ์ผ๋‹จ์€ Debug ํƒ€๊ฒŸ์„ 17.0์œผ๋กœ ์„ค์ •ํ•˜๋‹ˆ๊นŒ ๋นŒ๋“œ๋Š” ๋˜๋”๋ผ๊ตฌ์š”..

๋‚˜์ค‘์— Releaseํ•  ๋•Œ๋Š” ๋ณ„๋„์˜ ์ž‘์—…์„ ํ•ด์ฃผ์–ด์•ผ๊ฒ ์ง€๋งŒ ์šฐ์„ ์€ ์ด๋ ‡๊ฒŒ ์ง„ํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

#Preview๊ฐ€ ์ •๋ง ํŽธ๋ฆฌํ•œ ์ ์€ ViewController ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ UIView ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋‹ค์–‘ํ•œ ํฌ๊ธฐ์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ์‹œ๊ฐํ™”ํ•ด์ฃผ์–ด์„œ ํ˜น์‹œ ๋ชจ๋ฅผ ๋ฒ„๊ทธ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š”๋ฐ๋„ ํฐ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™๋„ค์š”.

ViewController Life Cycle

๋‹ค์Œ์œผ๋กœ๋Š” ViewController์˜ Life Cycle์— ๋ณ€๊ฒฝ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋Š”๋ฐ์š”.. viewIsAppearing์ด๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด viewIsAppearing์€ viewWillAppear์™€ viewDidAppear ์‚ฌ์ด์— ํ˜ธ์ถœ๋œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ์š”..!

๊ทธ๋ƒฅ viewWillAppear๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹ˆ๋ƒ! ํ–ˆ์ง€๋งŒ..

viewIsAppearing์ด ํ˜ธ์ถœ๋˜๋Š” ์‹œ์ ์€ UI์˜ trait collection์ด ๊ตฌ์„ฑ ๋˜์—ˆ๊ณ , ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์ด UI ๊ณ„์ธต(hierarchy)์— ์ถ”๊ฐ€๋˜๊ณ  ํ™•์ •๋œ ์‹œ์  ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

override func viewDidLoad() {
  super.viewDidLoad()
  self.view.addSubview(self.label)
  self.label.snp.makeConstraints { make in
    make.center.equalToSuperview()
  }
}

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  print("View will Appear: \(self.label.frame)")
}

override func viewIsAppearing(_ animated: Bool) {
  super.viewIsAppearing(animated)
  print("View is Appearing: \(self.label.frame)")
}

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  print("View did Appear: \(self.label.frame)")
}

์ž ๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ํ•ด๋‘๋ฉด ์–ด๋–ป๊ฒŒ ์ถœ๋ ฅ๋˜๋‚˜ ํ•œ ๋ฒˆ ๋ณผ๊นŒ์š”?

๐Ÿค” ๐Ÿค” ๐Ÿค” ๋ ˆ์ด์•„์›ƒ์ด ์•ˆ์žกํ˜€์žˆ๋Š”๋ฐ์š”..?

๋ญฅ๋ฏธ..

๋ผ๊ณ  ์‚ฝ์งˆ์„ ํ•˜๋˜ ์ค‘ ๋‹ค์Œ ํ™”๋ฉด์—์„œ ํ•ด๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

viewIsAppearing์€ LayoutSubviews๋“ค ๋ณด๋‹ค๋Š” ์•ž์— ์ฝœ๋ฐฑ๋˜๊ฑฐ๋“ ์š”! ๐Ÿซฃ

viewIsAppearing์ด ๋“ฑ์žฅํ•œ ๋ฐฐ๊ฒฝ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. viewWillAppear๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๊ณ„์ธต์— ์ถ”๊ฐ€๋˜๊ธฐ ์ด์ „์— ์ฝœ๋ฐฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— trait collection์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ view์˜ ์‚ฌ์ด์ฆˆ์™€ ์œ„์น˜์— ๋”ฐ๋ฅธ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ธฐ์—๋Š” ๋„ˆ๋ฌด ์ด๋ฆ…๋‹ˆ๋‹ค.
  2. viewDidAppear๋Š” ๋ชจ๋“  ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋๋‚œ ํ›„์— ์ฝœ๋ฐฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—…๋ฐ์ดํŠธํ•  ์š”์†Œ๋“ค์ด ๋„ˆ๋ฌด ๋Šฆ๊ฒŒ ์—…๋ฐ์ดํŠธ ๋˜๊ณ , ์œ ์ €๋“ค์€ ์ด๋ฅผ ์ง์ ‘ ๋ณด๊ฒŒ ๋œ๋‹ค.
  3. layoutSubviews๋“ค๊ณผ ๊ฐ™์€ ์‹œ๊ธฐ์— ์ฝœ๋ฐฑ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹คโ€ฆ!

๊ทธ๋Ÿฌ๋ฉด ๊ทธ๋ƒฅ layoutSubviews์—์„œ ์ž‘์—…ํ•˜๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€์š”? ๋ผ๋Š” ์งˆ๋ฌธ์ด ์ƒ๊ธฐ๋˜ ์ฐฐ๋‚˜ ๋ฐ”๋กœ ๋˜ ์„ค๋ช…ํ•ด์ฃผ์‹ญ๋‹ˆ๋‹ค.

layoutSubviews๋“ค์€ transition์ด ์žˆ์„ ๋•Œ, ํ˜น์€ view๊ฐ€ visibleํ•œ ๋™์•ˆ ๋ช‡ ๋ฒˆ์ด๊ณ  ํ˜ธ์ถœ ๋  ์ˆ˜ ์žˆ์ง€๋งŒ, viewIsAppearing์€ transition๋‹น ๋”ฑ ํ•œ ๋ฒˆ ๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ ˆ์ด์•„์›ƒ์„ ์žก๋Š” ๋™์ž‘์ด ํ•„์š”๊ฐ€ ์—†์„ ๋•Œ๋„ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ์šฉํ•œ ์ƒํ™ฉ๋„ ์žˆ๊ฒ ์ฃ !

Trait System Enhancements

์œ„ ๋‚ด์šฉ๊ณผ ์ด์–ด์ง€๋Š” ๋‚ด์šฉ์ธ ๊ฒƒ ๊ฐ™๋„ค์š”!

์ด๊ฑด ๊ทผ๋ฐ ์•„์ง ์ œ๊ฐ€ ํ•™์Šตํ•˜์ง€ ๋ชปํ•œ ๋‚ด์šฉ์ด๋ผโ€ฆ

์ด๋Ÿฐ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ๋งŒ ์•Œ์•„๋‘๊ณ  ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค..

๋งˆ์นจ ์„ธ์…˜ ์ค‘์— ๊ด€๋ จ ์„ธ์…˜์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„ ๊ฑฐ๊ธฐ์„œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณผ ๊ฒƒ ๊ฐ™๋„ค์š”!

์›๋ž˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ํ˜•ํƒœ์˜ trait๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ, ์ด์ œ๋Š” ์ปค์Šคํ…€ํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค! ๋ผ๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Animated Symbol Images

์• ํ”Œ์€ ์•„์ด์ฝ˜์„ SF Symbols๋ฅผ ํ†ตํ•ด ์ œ๊ณตํ•˜๊ณ  ์žˆ์ฃ ?

์ด ์•„์ด์ฝ˜๋“ค์„ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€์ ์ธ ๋…ธ๋ ฅ ์—†์ด ์•ฑ ์ „๋ฐ˜์ ์œผ๋กœ ํ†ต์ผ๋œ ์งˆ๊ฐ์„ ๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ํฐ ์žฅ์ ์ž…๋‹ˆ๋‹ค.

์ด์ œ ์ด SF Symbols์— ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ถ”๊ฐ€๋œ๋‹ค๊ณ  ํ•˜๋„ค์š”.

์‹ฌ์ง€์–ด ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” Symbol ์™ธ์—๋„ ์ปค์Šคํ…€ Symbol์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ํ•˜๋ƒ?.. ๊ธฐ๋ณธ์€ ์งฑ ์‰ฝ์Šต๋‹ˆ๋‹ค.

self.imageView.addSymbolEffect(.bounce)

์ด๋ ‡๊ฒŒ .bounce ์ดํŽ™ํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ํ•œ ๋ฒˆ bounce ๋ฉ๋‹ˆ๋‹ค.

ใ…‹ใ…‹ใ…‹ใ…‹ใ…‹ ๊ท€์—ฝ์ง€ ์•Š๋‚˜์š”..

self.imageView.addSymbolEffect(.variableColor.dimInactiveLayers)
self.imageView.removeSymbolEffect(ofType: .variableColor)

.bounce์™€๋Š” ๋‹ค๋ฅด๊ฒŒ .variableColor๋Š” ์ดํŽ™ํŠธ๋ฅผ ์ œ๊ฑฐํ•ด์ค„ ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ์š”!

์ด ์™ธ์—๋„ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ดํŽ™ํŠธ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋‚ด์šฉ ๋˜ํ•œ ๋ณ„๋„์˜ ์„ธ์…˜์œผ๋กœ ์ค€๋น„๋˜์–ด ์žˆ๋‹ค๊ณ  ํ•˜๋„ค์š”.

๊ฑฐ๊ธฐ์„œ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค!

Empty States

์ƒ๊ฐ์น˜๋„ ๋ชปํ•œ ๊ธฐ๋Šฅ์ด UIKit์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ ๋น„์–ด์žˆ๋Š” ํ™”๋ฉด Configuration!

๊ทผ๋ฐ ๊ทธ ์ „์— ์ด๊ฒƒ ์ข€ ๋ด์ฃผ์„ธ์š”..

๐Ÿ˜† ๐Ÿ˜† ๐Ÿ˜† ๋„ˆ๋ฌด ๊ท€์—ฌ์šฐ์…”โ€ฆ

๊ทธ๋ž˜์„œ ์ด๋ ‡๊ฒŒ ๊ท€์—ฝ๊ฒŒ ์†Œ๊ฐœํ•˜์‹  ๋‚ด์šฉ์ด ๋ญ๋ƒ๋ฉด์š”..

์ด๋ ‡๊ฒŒ ์ปจํ…์ธ ๊ฐ€ ์—†๋Š” ๋น„์–ด์žˆ๋Š” ํ™”๋ฉด ์ž…๋‹ˆ๋‹ค.

์ •๋ง ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์ปจํ…์ธ ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†๋Š” ๋“ฑ ํ™”๋ฉด์— ์•„๋ฌด๊ฒƒ๋„ ํ‘œ์‹œํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ๋“ค์ด ์žˆ์ฃ ?

var config = UIContentUnavailableConfiguration.empty()

config.image = UIImage(systemName: "star.fill")
config.text = "No Favorites"
config.secondaryText = "Your favorite translations will appear here."

viewController.contentUnavailableConfiguration = config

๊ทธ๋Ÿด ๋•Œ ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋น„์–ด์žˆ๋Š” ํ™”๋ฉด์— ๋Œ€ํ•œ ํ•ธ๋“ค๋ง์„ ํ•ด์ค„ ์ˆ˜๊ฐ€ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ViewController ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€์š”?

updateContentUnavailableConfiguration(using state:) ๋ฉ”์„œ๋“œ๋ฅผ overrideํ•ด์„œ ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

override func updateContentUnavailableConfiguration(
  using state: UIContentUnavailableConfigurationState
) {
  var config: UIContentUnavailableConfiguration?
  if self.searchResults.isEmpty {
    config = .search()
  }
  self.contentUnavailableConfiguration = config
}      

์ด๋ ‡๊ฒŒ contentUnavailableConfiguration์„ ๊ต์ฒดํ•ด์ฃผ๋Š” ๋กœ์ง๋“ค์„ ๋„ฃ์–ด์ฃผ๊ณ , ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๊ณณ์—์„œ

self.setNeedsUpdateContentUnavailableConfiguration()

๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์•„์ฃผ ๊ฐ„๋‹จํ•˜์ฃ !

Internationalization

๋‹ค์Œ ํŒŒํŠธ๋Š” ๊ตญ์ œํ™”์ž…๋‹ˆ๋‹ค.

์ด ํŒŒํŠธ๋Š” ์‚ฌ์‹ค ์ ‘ํ•  ๊ธฐํšŒ๊ฐ€ ๋งŽ์ง„ ์•Š์„ ๊ฒƒ ๊ฐ™์•„์„œ ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ„์•ผ๋งŒ ์‚ดํŽด๋ณผ๊ฒŒ์š”..

Wrapping and Hyphenation

์ „ ํŒŒํŠธ๊ฐ€ ์‚ฌ์‹ค ์•„๋ž ๋ฌธ์ž์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์ด๋ผ ํ .. ํ•˜๋ฉด์„œ ๋ณด๊ณ  ์žˆ์—ˆ๋Š”๋ฐ์š”.

์ด ํŒŒํŠธ๋กœ ๋„˜์–ด์˜ค๋ฉฐ ํ•œ๊ตญ์–ด๊ฐ€ ์–ธ๊ธ‰๋ผ์„œ ์—‡! ํ•˜๋ฉด์„œ ๋‹ค์‹œ ์ง‘์ค‘ํ•ด์„œ ๋ณด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ค‘๊ตญ์–ด, ๋…์ผ์–ด, ์ผ๋ณธ์–ด, ๊ทธ๋ฆฌ๊ณ  ํ•œ๊ตญ์–ด์˜ line-breaking ์— ํ–ฅ์ƒ์ด ์žˆ๋‹ค๊ณ  ํ•˜๋„ค์š”.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š”

let label = UILabel()
label.text = "์•ˆ๋…•ํ•˜์„ธ์š”!"
label.traitOverrides.typesettingLanguage = Locale.Language(identifier: "ko")     

์ด๋ ‡๊ฒŒ typesettingLanguage trait์„ ์„ค์ •ํ•ด์ฃผ๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ๋„ line-height์™€ hyphenation rule์„ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋„ค์š”.

UIImage๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ๋„ Locale์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํฌ๊ธฐ๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

UIImage์˜ font ํฌ๊ธฐ๊ฐ€ UILabel์˜ ํฌ๊ธฐ์™€ ๋งž์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์— ์ง์ ‘ ๊ฐ’์„ ๋น„๊ตํ•ด๊ฐ€๋ฉฐ ๋„ฃ๋Š๋ผ ๊ณ ์ƒํ•œ ๊ฒฝํ—˜์ด ์žˆ๋Š”๋ฐ, ๊ทธ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

let locale = Locale(languageCode: .korean)

imageView.image = UIImage(
  systemName: "character.textbox",
  withConfiguration: UIImage.SymbolConfiguration(locale: locale)    
)

์ž๋™์œผ๋กœ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ์ด๋ ‡๊ฒŒ locale์„ configuration์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

General Enhancements

์•„์ดํŒจ๋“œ ๋ถ€๋ถ„์€ ์šฐ์„  ์ญ‰ ๋„˜์–ด๊ฐ€๊ณ โ€ฆ

์ „๋ฐ˜์ ์ธ ํ–ฅ์ƒ ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

CollectionView Improvements

CollectionView๊ฐ€ ์„ฑ๋Šฅ์ ์œผ๋กœ ๋”! ํ–ฅ์ƒ๋˜์—ˆ๋‹ค๊ณ  ํ•˜๋„ค์š”..

๋Œ€๋žต 10000๊ฐœ์˜ ์•„์ดํ…œ์ด ์žˆ์„ ๋•Œ, iOS 16์— ๋Œ€๋น„ํ•ด์„œ ๋‘๋ฐฐ์ •๋„ ๋น ๋ฅด๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

10000๊ฐœ์˜ ์•„์ดํ…œ๊นŒ์ง€ ํ‘œํ˜„ํ•  ์ผ์ด ์žˆ์„๊นŒ..? ์‹ถ๊ธด ํ•œ๋ฐ.. ์•„๋ฌดํŠผ ๋นจ๋ผ์„œ ์•ˆ์ข‹์„ ๊ฑด ์—†์œผ๋‹ˆ๊นŒ์š”?

์ด ์ตœ์ ํ™”๋Š” Snapshot์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•, batchUpdate๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ๋ชจ๋‘์—๊ฒŒ ํ†ต์šฉ๋œ๋‹ค๋„ค์š”!

๊ทผ๋ฐ ์–ด๋–ป๊ฒŒ ํ•œ ๊ฑด์ง€๋Š” ์„ค๋ช…์„ ์•ˆํ•ด์„œ.. ๊ทธ๋ƒฅ ๊ทธ๋ ‡๋‹ค๊ณ  ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋ผ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹คโ€ฆ

๋‹ค์Œ์œผ๋กœ๋Š” CompositionalLayout ์˜ ํ–ฅ์ƒ์ ์ž…๋‹ˆ๋‹ค.

์ œ๊ฐ€ ์•„์ฃผ ์‚ฌ๋ž‘ํ•˜๋Š” CompositionalLayout.. ์–ผ๋งˆ๋‚˜ ๋ฐœ์ „ํ–ˆ๋‚˜ ๋ณผ๊นŒ์š”?

์ ์šฉ ์˜ˆ์‹œ๋กœ ์• ํ”Œ์ด ๊ฐ€์ ธ์˜จ ์•„์ดํŒจ๋“œ์˜ ๊ฑด๊ฐ• ์•ฑ์ž…๋‹ˆ๋‹ค.

๋‘ Item ๋ชจ๋‘ .estimated ๋ ˆ์ด์•„์›ƒ ์‚ฌ์ด์ฆˆ๋กœ ๊ตฌํ˜„๋˜์—ˆ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ์š”.. ์ž˜ ๋ณด๋ฉด ๋‘ ์•„์ดํ…œ์˜ ๋†’์ด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ Item ์ž์ฒด์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋‹ค๋ฅด์ง€๋งŒ, ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์—ฌ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‹จ ์˜์—ญ์ด ๋‚จ์•„์žˆ๋Š” ๋ชจ์Šต์ธ ๊ฒƒ์ด์ฃ .

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด layoutDimension์ด ์†Œ๊ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

uniformAcrossSiblings ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋“ฑ์žฅํ–ˆ๋„ค์š”.

NSCollectionLayoutDimension.uniformAcrossSiblings(estimate:) 

ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์•ˆ์— ์žˆ๋Š” ์•„์ดํ…œ๋“ค์ด ๊ฐ€์žฅ ํฐ ์•„์ดํ…œ์˜ ํฌ๊ธฐ์— ๋งž์ถฐ์ง€๋Š” ๊ฒƒ ์„ ๋ณผ ์ˆ˜ ์žˆ๋„ค์š”.

์ด๋ ‡๊ฒŒ ๋ ์ง€์ ์ด ๋‹ฌ๋ผ์„œ ๋ถˆํŽธํ–ˆ๋˜ ๋ ˆ์ด์•„์›ƒ์„

๊ทธ๋ฃน ๋‚ด์˜ ๊ฐ€์žฅ ํฐ ์•„์ดํ…œ์— ๋งž์ถฐ ๋†’์ด๋ฅผ ํ†ต์ผ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํŽธ์•ˆโ€ฆ ๐Ÿ˜Œ

Spring Animation Parameters

Spring Animation์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‘ ๊ฐ€์ง€๋กœ ์ค„์ด๋Š” ํ–ฅ์ƒ์ด ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

duration ๊ณผ bounce ์ด๋ ‡๊ฒŒ ๋‘ ๊ฐ€์ง€๋กœ์š”.

duration์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ „์ฒด์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด ์•„๋‹ˆ๋ผ ๋ณ€ํ™˜ ์œ„์น˜์— ๋‹ค๋‹ค๋ฅด๋Š” ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. bounce์™€๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค๊ณ  ํ•˜๋„ค์š”.

bounce๋Š” ๋ง ๊ทธ๋Œ€๋กœ ํŠ•๊ธฐ๋Š” ์ •๋„๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ์ž…๋‹ˆ๋‹ค.

์ด ๋‘ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” UIView.animate์— ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UIView.animate(springDuration: 0.5, bounce: 0.5) {
  self.circle.center.x += 100
}

์ด ๋‘ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” Optional ํŒŒ๋ผ๋ฏธํ„ฐ๋ผ์„œ

UIView.animate {
  self.circle.center.x += 100
}

์ด๋ ‡๊ฒŒ ํŒŒ๋ผ๋ฏธํ„ฐ ์—†์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์‹œ์Šคํ…œ์ด ์ •ํ•ด๋‘” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

Text Interactions

ํ…์ŠคํŠธ ์ž…๋ ฅ ์ปค์„œ์™€ ํ…Œ์ŠคํŠธ ์„ ํƒ์— ์ƒˆ๋กœ์šด API๋“ค์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

์ด์ œ UITextInteraction์„ ํ†ต์งธ๋กœ ๊ฐ€์ ธ์™€ ์ ์šฉํ•˜๋Š” ๋Œ€์‹ , ์„ ํƒํ•œ ํ…์ŠคํŠธ์— ๋Œ€ํ•œ UI๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด API๋“ค์€ UITextViewDelegate๋ฅผ ์ฑ„ํƒํ•˜๋ฉด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, link์™€ text attachments๋ฅผ ๋” ์ž์œ ์ž์žฌ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ link๊ฐ€ ์•„๋‹Œ ํ…์ŠคํŠธ์— ๋Œ€ํ•ด์„œ๋„ ์ธํ„ฐ๋ž™์…˜ ์•ก์…˜์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” API๋„ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๊ณ  ํ•˜๋„ค์š”!

๋‹ค๋งŒ ์ด ๋ถ€๋ถ„์€ ์ œ๊ฐ€ ์ ์šฉํ•ด๋ณธ ์ ์ด ์—†์–ด์„œ ์ œ๋Œ€๋กœ ์ดํ•ดํ•œ ๊ฒƒ์ธ์ง€ ๋ชจ๋ฅด๊ฒ ๋„ค์š”..

Default StatusBar Style

์ด๋Ÿฐ ๊ฒฝ์šฐ ์•„์ฃผ ๋งŽ์„ ๊ฒ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ ๋ผ์ดํŠธ ๋ชจ๋“œ๋ผ์„œ ๊ฒ€์ •์ƒ‰์˜ StatusBar๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ปจํ…์ธ ๋ฅผ ์œ„๋กœ ์Šคํฌ๋กคํ•˜๋ฉด..?

์จ”์ž”.. ์ด๋ ‡๊ฒŒ ์ปจํ…์ธ ์™€ StatusBar๊ฐ€ ๊ฒน์ณ๋ณด์ด๋ฉด์„œ ์›๋ž˜๋ผ๋ฉด ๊ฒ€์ •์ƒ‰์˜ StatusBar๊ฐ€ ์•ˆ๋ณด์ด๊ฒŒ ๋๊ฒ ์ง€๋งŒ,

override var preferredStatusBarStyle: UIStatusBarStyle {
  return .default
}

์ด๋ ‡๊ฒŒ .default๋กœ ์„ค์ •ํ•ด๋‘๋ฉด ์•Œ์•„์„œ ๋’ค์— ๋ฐฐ์น˜๋œ ์ปจํ…์ธ ์— ๋”ฐ๋ผ ์ƒ‰์ƒ์„ ๋ฐ”๊พผ๋‹ค๊ณ  ํ•˜๋„ค์š”.

์‹ฌ์ง€์–ด ์ด ๋ณ€ํ™”๋Š” ์–‘์ชฝ์ด ๋ณ„๊ฐœ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์•„์ฃผ ๊ตฟ! ๐Ÿ‘

Drag and Drop Enhancements

์ด ๋ถ€๋ถ„์€ ์ง์ ‘ ๋ณด๋Š”๊ฒŒ ๋น ๋ฅผ ๊ฒƒ ๊ฐ™์•„์š”!

์ด์ œ ๋“œ๋ž˜๊ทธ & ๋“œ๋กญ์ด ๊ฐ€๋Šฅํ•œ ์ปจํ…์ธ ๋Š” ์ปจํ…์ธ ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์•ฑ ์•„์ด์ฝ˜์— ๋“œ๋กญํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ๋„ ํŠน์ • ์•ก์…˜์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Info.plist ์˜ CFBundleDocumentTypes์— ํ•ด๋‹น ํŒŒ์ผ์˜ ํ˜•์‹์ด ์ง€์›ํ•˜๋Š” ์ง€๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์„ค์ •๋œ ํ˜•์‹์˜ ํŒŒ์ผ์€ UIScene delegate ์ฝœ๋ฐฑ์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋œ๋‹ค๊ณ  ํ•˜๋„ค์š”!

ISO HDR Image Support

UIKit์—์„œ ์ด์ œ UIImageView๋‚˜ UIGraphicsImageRenderer์—์„œ ISO HDR ์ด๋ฏธ์ง€๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„๋„ ๊ด€๋ จ ์„ธ์…˜์—์„œ ์ž์„ธํ•˜๊ฒŒ ๋‹ค๋ฃฌ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค..

PageControl

UIPageControl์€ ์ž๋™ ์ „ํ™˜๋˜๋Š” ๋ฐฐ๋„ˆ๋กœ ๋งŽ์ด ์“ฐ์ด๊ณ  ์žˆ์ฃ .

๊ทธ ์“ฐ์ž„์ƒˆ๋ฅผ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก UIPageControl์— ํƒ€์ด๋จธ ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ ์ ๋งŒ ์žˆ๋˜ ๊ฒƒ์„ ๋„˜์–ด ์ด์ œ ํ˜„์žฌ ํŽ˜์ด์ง€๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ง„ํ–‰๋˜์—ˆ๋Š”์ง€ ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋„ค์š”.

let timerProgress = UIPageControlTimerProgress(preferredDuration: 10)
pageControl.progress = timerProgress

timerProgress.resumeTimer()

UIPageControl์˜ ์ƒˆ๋กœ์šด progress ํ”„๋กœํผํ‹ฐ๋ฅผ ํ™œ์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ฌด๋ คโ€ฆ ๋นŒํŠธ์ธ ํƒ€์ด๋จธ ๊ฐ€ ์ƒ๊ฒผ๋‹ค๊ณ  ํ•ด์š”..

ํƒ€์ด๋จธ๊ฐ€ ๋  ๋•Œ๋งˆ๋‹ค ๋‹ค์Œ ํŽ˜์ด์ง€๋กœ ์•Œ์•„์„œ ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์€ ๋ฌผ๋ก ์ด๊ณ ,

myTimer.addPeriodicTimeObserver { timer in
  progress.currentProgress = Float(timer.seconds / timer.duration)
  // ..
}

์ด๋ ‡๊ฒŒ ์ง์ ‘ progress๋ฅผ ์„ค์ •ํ•ด์ค„ ์ˆ˜๋„ ์žˆ์–ด์š”!

์ด์ œ ์ž๋™ ๋ฌดํ•œ ์Šคํฌ๋กค ๋ฐฐ๋„ˆ๋ฅผ ์•„์ฃผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”.

Palette Menus

์•„๋‹ˆ ์ง„์งœ ์ด๋ถ„ ์™œ ์ด๋ ‡๊ฒŒ ๊ท€์—ฌ์šฐ์‹  ๊ฑด๋ฐ์š”..

์•„๋ฌดํŠผ..

์ด๋Ÿฐ ์ƒ‰์ƒ์„ ์„ ํƒํ•˜๋Š” ๋ฉ”๋‰ด๊ฐ€ ์ง€๊ธˆ๋„ ์ด๊ณณ์ €๊ณณ์— ์žˆ์—ˆ์ฃ ..?

์ด์ œ Palette ๋ฉ”๋‰ด๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

UIMenu(options: [ .displayInline, .displayAsPalette ], children: [ ... ])   

์ด๋ ‡๊ฒŒ UIMenu์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ‰์ƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ

UIAction(image: UIImage(systemName: "heart.fill"), state: .on) { ... }

์ด๋ ‡๊ฒŒ ์ด๋ฏธ์ง€๋„ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋„ค์š”.

์ด๋ฏธ์ง€์˜ ๊ฒฝ์šฐ .on ์ƒํƒœ์ผ ๋•Œ tintColor๋กœ ํ•˜์ด๋ผ์ดํŠธ ๋œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ โ€œWhatโ€™s new in UIKitโ€ ์„ธ์…˜์˜ ๋‚ด์šฉ์ด์˜€์Šต๋‹ˆ๋‹ค.

Preview์™€ StatusBar, Progress๊ฐ€ ์ ‘๋ชฉ๋œ PageControl..

๋งˆ์Œ์— ๋“œ๋Š” ๋‚ด์šฉ๋“ค์ด ์ž”๋œฉ ์žˆ์—ˆ๋„ค์š”. ๐Ÿ‘

๋‹ค๋ฅธ ์„ธ์…˜๋„ ์–ผ๋ฅธ ๊ณต๋ถ€ํ•˜๋Ÿฌ ์ถœ๋™!

References

WWDC23 - Whatโ€™s new in UIKit