ํ๋ก์ ํธ ๊ธฐ๊ฐ 2022.04.11 ~ 2022.4.22 ํ์ : ๐ข@Doogie ๐ด@cherrishRed / ๋ฆฌ๋ทฐ์ด : @TTOzzi
๋ง๊ตญ๋ฐ๋ํ์ ์ค๋ช ์ ํ์ธํ์ธ์! ์ด๋ค ํ๊ตญ์ ์ํ๋ค์ด ์๋์ง ์์ธํ ๋ณผ ์ ์์ด์! ํฐ ๊ธ์จ๋ ์ง์ํฉ๋๋ค!
JSON
,MetaType
,TableView
,DynamicType
,NSMutableAttributedString
,์์กด์ฑ ์ฃผ์
Item
,Exposition
๊ตฌ์กฐ์ฒด ๊ตฌํDecodable
ํ์ฅ์ ํตํdecode
๋ฉ์๋ ๊ตฌํ
์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ์ ๊ทผ์ ์ด๋ฅผ ์ค์ ํด์ฃผ๊ณ ์ private(set)์ผ๋ก ์ค์ ํ๋ ค๊ณ ํ์ผ๋ ์ค๋ฅ๊ฐ ๋ฌ๋ค.
โ let ์์ฒด๊ฐ ์ฝ๊ธฐ ์ ์ฉ์ด๋ผ ์ธ๋ถ์์ ๋ณด์์ ๋๋ private(set) ๊ณผ ๋์ผํด, ์ ๊ทผ์ ์ด๋ฅผ ๊ฑธ์ด์ค ํ์๊ฐ ์๋ ๋ฌธ์ ์๋ค.
model VS viewcontroller ์ผ๋ฐ์ ์ธ ๋์ฝ๋ฉ ์์ ์ฝ๋์์๋ decode ๋ฅผ View Controller ์์ ๋ง์ด ์ฒ๋ฆฌ ํ๋ ๊ฒ์ ํ์ธ ํ์ผ๋ decoder ์์๋ UI ๋ฅผ ๊ฑด๋๋ฆฌ๋ ๋ถ๋ถ์ด ์์ด ์ด๋์ ์์นํด์ผ ํ๋์ง ์๋ฌธ์ด์๋ค.
โ
์ ํฌ์ ํ๋จ์ผ๋ก๋ ๋ฐ์ดํฐ๋ฅผ ๊ถ์ฅํ๋ ๋ถ๋ถ์ด View Controller ์๋ ๊ฒ์ด ์ข์ง ์๋ค๊ณ ํ๋จํด Decodable
์ ํ์ฅํด ๊ธฐ๋ฅ์ ๊ตฌํํด ์ฃผ๊ฒ ๋์๋ค.
[Self]? == Decodable? ๋ก ์ธ์์ด ๋ผ์ ์ ์์ ์ผ๋ก ๋ฐํ์ด ๋๋ ๊ฒ ๊ฐ์๋ฐ ์ ๋ฐฐ์ดํ์
๊ณผ Decodableํ์
์ด ๋์ผ ํ์
์ผ๋ก ์ทจ๊ธ๋๋ ๊ฒ์ธ์ง ์๋ฌธ์ด์๋ค.
โ
๊ณต์๋ฌธ์๋ฅผ ์ฐพ์๋ณด๋ ๋ฐฐ์ด์ ๋ด๋ถ์์๊ฐ ๋ชจ๋ Decodable ํ์
์ ์ฑํํ๋ฉด ๊ทธ ๋ฐฐ์ด๋ Decodable ์ ์ฑํํ๋ค๊ณ ํ๋ค๋ ์ ์ ๋ฐ๊ฒฌํ๋ค.
JSONDecode.decode ๋ฅผ ํ ๊ฒฝ์ฐ DecodingError ๊ฐ ๋ฐ์ํ๋ค๋ ์ฌ์ค์ ์๊ฒ ๋์๋ค.
์ ์ ํ case ์ ๋ง์ถฐ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ํ
์คํธ๋ฅผ ์งํํ๋ ค๊ณ ํ์ผ๋ ์๋ฌ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ด ์๋ฌธ์ด์๋ค.
โ Error ์ ์ ๊ทผํ ๋ฐฉ๋ฒ
func test_Exposition_decodeํธ์ถ์_์๋ชป๋ํ์ผ๋ช
์๋์
ํ ์_assetLoadError๊ฐ๋ฐ์ํ๋์ง() throws {
XCTAssertThrowsError(try Exposition.decode(with: "์ด์ํ์ด๋ฆ")) { error in
XCTAssertEqual(error as? DataLoadError, DataLoadError.assetLoadError)
}
}
์ ํฌ๊ฐ ์์ฑํ ์๋ฌ ํ์
์ธ DataLoadError
์ ์ ๊ทผ ํ์ ๋์๋ XCTAssertThrowsError
๋ฉ์๋๋ก ์๋ฌ๋ฅผ ๋ฐ๊ณ XCTAssertEqual
๋ก ๋น๊ตํด ํ
์คํธ๋ฅผ ์ฑ๊ณตํ ์ ์์๋ค.
ํ์ง๋ง DecodingError๋ Equatable์ ์ฑํํ๊ณ ์์ง ์์ ๋น๊ต๊ฐ ๋ถ๊ฐ๋ฅ ํ๋ค.
func test_ํ๋กํผํฐ์ํ์
์ด์๋ชป๋๊ฐ์ฒด์์_decodeํธ์ถ์_typeMismatch์๋ฌ๊ฐ๋ฐ์ํ๋์ง() throws {
do {
try DummyForWrongType.decode(with: "exposition_universelle_1900")
XCTFail()
} catch DecodingError.typeMismatch(let key, let value) {
print(print("\n key: \(key) \n value: \(value) \n"))
} catch {
XCTFail()
}
}
๊ทธ๋์ do try ๋ฌธ์ ์ฌ์ฉํด์ ์ํ๋ ์๋ฌ๊ฐ ๋ฐ์๋ ๊ฒฝ์ฐ ํด๋น ๋ด์ฉ์ ํ๋ฆฐํธ ํด ํต๊ณผํ๊ฒ ๋ง๋ค์๊ณ ์ํ๋ ์๋ฌ๊ฐ ๋ฐ์๋์ง ์๋ ๊ฒฝ์ฐ์๋ ์คํจํ๋๋ก ํ ์คํธ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์๋ค.
- JSON
- Meta Type
- Asset
View
๊ตฌ์ฑ,ViewController
๊ตฌ์ฑTable
๋ทฐ ๊ตฌ์ฑJSON
๋ฐ์ดํฐ๋ฅผ ๋ทฐ์ ๋์๊ธฐ
์คํฌ๋กค ๋ทฐ์ ๋์ด๋ฅผ ๋ฐ๋ก ์ค์ ํด ์ฃผ์ง ์์ผ๋ฉด Y positin or height
๊ฐ ํ์ํ๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ ํ๋ค
โ ์ด ์ค๋ฅ์ ํด๊ฒฐ ๋ฐฉ์์ผ๋ก ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ํ์ธํ๋ค.
๋ ๋ฐฉ๋ฒ ์ค Y ์ถ๋ง ์ง์ ํด๋ ๋๊ธฐ ๋๋ฌธ์ ์ฒซ๋ฒ ์งธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ๋ค.
func showAlert() {
let alert = UIAlertController(title: "์ค๋ฅ", message: "๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.", preferredStyle: .alert)
let action = UIAlertAction(title: "ํ์ธ", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
ํ์ฌ ํ๋ก์ ํธ์์ ์์ ๊ฐ์ด ์ผ๋ฟ์ ๋ํ๋ด๋ ๋ฉ์๋๋ฅผ ๋ ๊ฐ์ ๋ทฐ์ปจํธ๋กค๋ฌ์ ๊ฐ๊ฐ ํ๋์ฉ ๊ตฌํ๋์ด ์๋๋ฐ ์ง๊ธ ํ๋ก์ ํธ์์๋ ํ๋ฒ์ฉ ๋ฐ์ ์ฌ์ฉ์ ํ์ง ์์์ ๊ฐ ๋ทฐ์ปจํธ๋กค๋ฌ์ ์์ฑ์ ํ์ง๋ง ์ด๋ฐ ์ฌ์ฌ์ฉ์ฑ์ด ์๋๋ฐ ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ ํ๋์ ํ์ผ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌ๋ฅผ ํด์ค๋ ๊ด์ฐฎ์์ง ๊ณ ๋ฏผ์ด์๋ค.
โ
๊ฒฐ๊ตญ UIViewController
์ extension
์ showAlert()
๋ฉ์๋๋ฅผ ๋ง๋ค์ด ์ฌ์ฌ์ฉ์ฑ์ ๋์ฌ์ฃผ๋ ๋ฐฉ๋ฒ์ ์ฑํํ์ต๋๋ค.
defaultContentConfiguration
TableView
TableViewDataSource
TableVeiwDelegate
ScrollView
Dynamic Type
์ ์ฉDynamic Type
์ ๋ฐ๋ฅธ ์คํ๋ทฐ axis์ ํ- ์ ์ฒด์ ์ธ AutoLayout ์์
- ์ฒซ๋ฒ์จฐ ํ๋ฉด ์ธ๋ก๋ชจ๋๋ก ๊ณ ์ , ๊ฐ๋ก๋ชจ๋ ์ธ๋ถ ์ค์
[๊ธฐ์กด ์ฝ๋]
final class ItemViewController: UIViewController {
@IBOutlet weak private var itemImageView: UIImageView!
@IBOutlet weak private var descriptionLabel: UILabel!
var item: Item?
์ ์ฝ๋์์ item์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ฃผ๊ธฐ ์ํด์๋ ItemViewController
์ ์ธ์คํด์ค๋ฅผ ์์ฑํด ์ง์ ์ ๊ทผ์ ํด์คฌ์ด์ผ ํ๋๋ฐ ๊ทธ๋ฌ๋ฉด ์ธ๋ถ์์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ง ์๋๋ก ํ๋ ์ ๊ทผ์ ์ด ์ค์ ๋ ํ์ง ๋ชปํ๊ณ ๋ ์ง์ ์ ๊ทผํ๋ค๋ ๊ฒ์ ๋ฌธ์ ์ ์ผ๋ก ๋ง์ํด ์ฃผ์
จ๊ณ storyboard creator init
์ ๋ํด ๊ณต๋ถํด๋ณด๊ธธ ๊ถ์ ํด์ฃผ์
จ๋ค
final class ItemViewController: UIViewController {
@IBOutlet weak private var itemImageView: UIImageView!
@IBOutlet weak private var descriptionLabel: UILabel!
private let item: Item
init(item: Item) {
self.item = item
super .init(nibName: nil, bundle: nil)
}
init?(_ coder: NSCoder, _ item: Item) {
self.item = item
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
โ
๊ทธ๋์ ๊ณต๋ถ ํ ์ ๊ณผ์ ์ ํตํด ItemViewController
์ init์ ํตํด item์ ํ ๋นํด์ฃผ๋๋ก ํ๋ค.
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fatalError๋ ์ผ๋ถ๋ฌ ํฌ๋์ฌ๋ฅผ ๋ด๋ ๋ฉ์๋๋ก ์ฌ์ฉ์ ๋ฆฌ์ ์ฌ์ ์ ํด๋นํ๋ค๊ณ ํ๋ค (์๊ฐํด๋ณด๋ ์ผ๋ถ๋ฌ ํฌ๋์ฌ๋ฅผ ๋ด๋ ํ์ ์์ฒด๊ฐ ๋ง์ด ์๋๋ ๋ฏ..)
required init?(coder: NSCoder) {
super.init(coder: coder)
}
๊ทธ๋์ ์์ ๊ฐ์ด super๋ฅผ ํตํด ๊ธฐ์กดํด ํด์ฃผ๋ ์์ ๋ง ์งํ ํ ์ ์๊ฒ ํด์ฃผ์์ผ๋ฉฐ ํธ์ถ ๋ถ์์๋
let itemVC = storyboard
.instantiateViewController(identifier: ItemViewController.identifier,
creator: { coder -> ItemViewController? in
return .init(coder, item)
})
์์ ๊ฐ์ด ์์ ํด ๋ฐํ๊ฐ์ ์ต์ ๋๋ก ์์ ํด ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด nil์ ๋ฐํํ ์ ์๊ฒ ํด์ฃผ์๋ค.
์ด์๋ฐ๋ผ
init(item: Item) {
self.item = item
super .init(nibName: nil, bundle: nil)
}
์ฒ์์๋ nil์ ๋ฐํํ๋ฉด ์๋ ๊ฒ ๊ฐ์์ nil์ด๋ฉด ์ ์ด๊ธฐํ ๋ฉ์๋๋ฅผ ํตํด ๋น ๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ ๋ฐํํด์ฃผ๋ ค๊ณ ํ๋๋ฐ ์ ์ฝ๋๋ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ ์ ๊ฑฐ
Heritage
์ ListViewController
์ฌ์ด์ ์์กด๋๊ฐ ๋๋ฌด ๋์ ๊ฒ ๊ฐ์ ์์กด์ฑ์ ๋ฎ์ถฐ๋ณด๊ธฐ๋ก ํ์๋ค.
โ
Item
์ด๋ผ๋ ํ๋กํ ์ฝ์ ์์ฑํด ์์กด๋๋ฅผ ๋ฎ์ถฐ ๋ณด์๋ค.
Heritage
-> <-ListViewController
์ด๋ฐ ๊ตฌ์กฐ๋ฅผ
Heritage
-> Item
<-ListViewController
์ด๋ ๊ฒ ์์ ํ๋ค.
accessability
์ dynamic font
๋ฅผ ์ง์ํ์ต๋๋ค.
ํฐํธ์ ํฌ๊ธฐ๊ฐ ๋๋ฌด ์ปค์ง๋ฉด UI์ ๊ฐ๋
์ฑ์ด ํ์ ํ ๋จ์ด์ง๋ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๋ค.
โ
- main View ์ ๋ฒํผ์ด ์์ ์ปจํ
์ธ ์ ๊ฒน์น์ง ์๋๋ก
maximumContentSizeCategory
์ฌ์ด์ฆ๋ฅผ.accessibilityExtraLarge
๋ก ์ค์ ํด ์ฃผ์์ต๋๋ค. - 2๋ฒ ์งธ ์คํฌ๋กค ๋ทฐ์
accessibilityExtraLarge
์ฌ์ด์ฆ ์ด์์ ๋ทฐ์์๋ ์ฌ์ง๊ณผ ๊ธ์จ์ ๊ฐ๋ ์ฑ์ ์ํด์ stackView ๋ฅผ ์ธ๋ก๋ก ๋๋ฆฌ๋๋ก ์ค์ ํ๋ค.
โ ์ฒซ๋ฒ์งธ ํ๋ฉด์ ๊ณ ์ ํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ๋ค์ ์ฌ์ฉํ์ต๋๋ค.
- mainViewController ์์ AppDelegate ๋ฅผ ๋ฐ์์์ ๊ณ ์ ์ ํด์ฃผ์์ต๋๋ค.
- AppDelegate ๋ด๋ถ์์ mainViewController ๋ฅผ ๋ฐ์์ ๊ณ ์ ํด์ฃผ๊ธฐ
- AppDelegate ์ mainViewController ์ฌ์ด์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ๊ทธ ๊ฐ์ฒด์์ ๊ณ ์ ํด์ฃผ๋ ๋ฉ์๋๋ฅผ ์์ฑํ๊ธฐ
supportedInterfaceOrientations
๋ฉ์๋๋ฅผ override ํด์ ํ๋ฉด ๊ณ ์ ํ๊ธฐ
1, 2, 3 ๊ณผ ๊ฐ์ ๋ค์ํ ๋ฐฉ๋ฒ์ ์๋ํด ๋ณด์์ง๋ง AppDelegate ์ mainViewController ์ฌ์ด์ ์์กด์ฑ์ด ๋์ ์ง๋ค๋ ์ ๋ฑ์ ๋จ์ ์ผ๋ก ์ฌ์ฉํ์ง ์์๊ณ ๊ฒฐ๊ตญ์ ExpoNavigationController
์ mainViewController
์ ์๋์ ๋ฉ์๋๋ฅผ ๊ตฌํํด ์ฃผ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
guard let _ = self.topViewController as? MainViewController else {
return .all
}
return .portrait
}
๊ฐ๋ก์ํ์์ 2๋ฒ์งธ ํ๋ฉด์ผ๋ก ํ๋ฉด ํ์ ์ด ์ผ์ด๋ฌ์ ๋ ํ์ ์ด ๋๋ ์ํฉ์ ๋ง๋ค๊ธฐ ์ํด์
viewDidAppear
์ attemptRotationToDeviceOrientation
๋ฉ์๋๋ฅผ ํธ์ถํด ์ฃผ์๋ค.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIViewController.attemptRotationToDeviceOrientation()
}
func changeItemStackViewSetting(){
let category = UIApplication.shared.preferredContentSizeCategory
switch category {
case UIContentSizeCategory.accessibilityExtraLarge, UIContentSizeCategory.accessibilityExtraExtraLarge, UIContentSizeCategory.accessibilityExtraExtraExtraLarge:
itemStackView.axis = .vertical
labelStackView.leadingAnchor.constraint(equalTo: itemStackView.leadingAnchor, constant: 0.0).isActive = true
labelStackView.trailingAnchor.constraint(equalTo: itemStackView.trailingAnchor, constant: 0.0).isActive = true
itemImageView.widthAnchor.constraint(equalTo: itemStackView.widthAnchor, multiplier: 0.6).isActive = true
default:
itemStackView.axis = .horizontal
}
}
์์ฝ๋๋ ๊ธ์จ ํฌ๊ธฐ๊ฐ ๋ฐ๋์ ๋ฐ๋ผ cell์ ์ ์ฝ ์กฐ๊ฑด์ ๋ฐ๊ฟ์ฃผ๋ ๊ธฐ๋ฅ์ธ๋ฐ ์ฌ์ค ์ด๋ ์ ์ฝ ์กฐ๊ฑด์ ๋ฐ๊ฟ์ฃผ๋ ๊ฒ์ด ์๋ ๊ธ์จ ํฌ๊ธฐ๊ฐ ๋ฐ๋ ๋ ๋ง๋ค ์ ์ ์ฝ ์กฐ๊ฑด์ ๋ง๋ค์ด ์ฃผ๋ ๊ฒ์ด๋ผ ๊ณ์ ํด์ ์์ด๊ฒ ๋๋ค.
โ
private lazy var labelLeadingConstraint = labelStackView.leadingAnchor.constraint(equalTo: itemStackView.leadingAnchor, constant: 0.0)
private lazy var labeltraillingConstraint = labelStackView.trailingAnchor.constraint(equalTo: itemStackView.trailingAnchor, constant: 0.0)
private lazy var ImageViewWidthConstraint = itemImageView.widthAnchor.constraint(equalTo: itemStackView.widthAnchor, multiplier: 0.6)
func changeItemStackViewSetting(){
let category = UIApplication.shared.preferredContentSizeCategory
switch category {
case UIContentSizeCategory.accessibilityExtraLarge, UIContentSizeCategory.accessibilityExtraExtraLarge, UIContentSizeCategory.accessibilityExtraExtraExtraLarge:
itemStackView.axis = .vertical
labelLeadingConstraint.isActive = true
labeltraillingConstraint.isActive = true
ImageViewWidthConstraint.isActive = true
default:
itemStackView.axis = .horizontal
labelLeadingConstraint.isActive = false
labeltraillingConstraint.isActive = false
ImageViewWidthConstraint.isActive = false
}
}
๊ทธ๋์ ๊ฐ๊ฐ์ ์ ์ฝ ์กฐ๊ฑด์ ๋ณ์๋ก ์์ฑํ๊ณ ์ด๋ฅผ ํ์ฑํ/๋นํ์ฑํ ์์ผ์ฃผ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํ์๋ค.
private func changeFontSize(for expoInfo: Exposition) {
let bodyFont = UIFont.preferredFont(forTextStyle: .body)
visitorsLabel.changeFontSize(bodyFont, targetString: "๋ฐฉ๋ฌธ๊ฐ")
locationLabel.changeFontSize(bodyFont, targetString: "๊ฐ์ต์ง")
durationLabel.changeFontSize(bodyFont, targetString: "๊ฐ์ต ๊ธฐ๊ฐ")
}
private func changeFontSize(for expoInfo: Exposition) {
let bodyFont = UIFont.preferredFont(forTextStyle: .body)
visitorsLabel.changeFontSize(bodyFont, targetString: ": \(ExpoNumberFormatter.changeVisitorsFormat(from: expoInfo.visitors) ?? "์ ๋ณด ์์")")
locationLabel.changeFontSize(bodyFont, targetString: ": \(expoInfo.location)")
durationLabel.changeFontSize(bodyFont, targetString: ": \(expoInfo.duration)")
}
์ต์ด์ "๋ฐฉ๋ฌธ๊ฐ", "๊ฐ์ต์ง" ๋ฑ ์ด๋ฐ ์ ๋ชฉ? ๊ฐ์ ๊ณณ์ ๋ค๋ฅธ ํฐํธ๋ฅผ ์ ์ฉํด ์ฃผ๋ ๊ฒ์ด ์ฝ๋๋ ๊ธธ์ด์ง์ง ์๊ณ ๋ณด๊ธฐ ์ข์ ๊ฒ ๊ฐ์ ๊ทธ๋ ๊ฒ ์งํ ํ์ผ๋ ๊ตฌ๋ ํ๋ฉด์์๋ ๋ค์ชฝ ๋ถ๋ถ์ด dynamic type ์ ์ฉ์ด ๋์ง ์๋ ๋ฌธ์ ๊ฐ ์์๋ค
๋ฆฌ๋ทฐ์ด์๊ฒ ๋ฌผ์ด๋ณด๋
attributedText๊ฐ ์ ์ฉ๋๋ ์์ ์ ์ฒซ ๋ฒ์งธ ๋ฌธ์์ ์คํ์ผ ์ ๋ณด๋ก UILabel์ ์คํ์ผ ๊ด๋ จ ํ๋กํผํฐ๋ค์ ์
๋ฐ์ดํธ ํ๋ค๊ณ ํฉ๋๋ค!
๋ผ๋ ๋ง๋ก ๋ฏธ๋ฃจ์ด ๋ณด์ ์ ์ฉ๋๋ ๋ฒ์ ์ดํ์ ๋ฌธ์ ์คํ์ผ์ ์ ์ฉ์ด ๋์ง ์์ ์์ ๊ฐ์ ํ์์ด ์ผ์ด๋ ๊ฒ ๊ฐ๋ค
์ฒ์๋ถํฐ ์๋ฎฌ๋ ์ดํฐ๋ฅผ ๊ฐ๋ก๋ก ๋๊ณ ์ดํ์ ์คํ์ํค๋ฉด 2๋ฒ ํ๋ฉด์ผ๋ก ๋์ด๊ฐ ๋ ํ๋ฉด ํ์ ์ด ์ผ์ด๋์ง ์์
-> ์ต์ด ์คํ์ ๊ธฐ๊ธฐ์ ๋ฐฉํฅ์ด ๋ณ๊ฒฝ๋๊ธฐ ์ ์๋ unknown์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ํ์ฌ ๊ธฐ๊ธฐ์ ํ๋ฉด ์ํ๋ฅผ ์ฒดํฌํด์ฃผ๋ ๋ก์ง์ ๋ง๋ค์ด์ผ ํ๋๋ฐ ์ดํ ์ฌ์ฉ์๋ ํฐ ๋ฌธ์ ๊ฐ ๋์ง ์์ ๊ตฌํํ์ง ์์
โ
์์กด์ฑ
ViewController์ init
dynamic type
NSMutableAttributedString
์ปค๋ฐ ์ ๋ชฉ์ ์ต๋ 50์ ์ ๋ ฅ ๋ณธ๋ฌธ์ ์ต๋ 72์ ํ๊ธ๋ก ์ ๋ ฅ
๐feat : ์๋ก์ด ๊ธฐ๋ฅ ๊ตฌํ
โ๏ธchore : ์ฌ์ํ ์ฝ๋ ์์ , ๋ด๋ถ ํ์ผ ์์ , ํ์ผ ์ด๋ ๋ฑ
๐จfix : ๋ฒ๊ทธ, ์ค๋ฅ ํด๊ฒฐ
๐docs : README๋ WIKI ๋ฑ์ ๋ฌธ์ ๊ฐ์
โป๏ธrefactor : ์์ ์ด ์์ ๋ ์ฌ์ฉ (์ด๋ฆ๋ณ๊ฒฝ, ์ฝ๋ ์คํ์ผ ๋ณ๊ฒฝ ๋ฑ)
โฐ๏ธdel : ์ธ๋ชจ์๋ ์ฝ๋ ์ญ์
๐ฌtest : ํ ์คํธ ์ฝ๋ ์์
๐ฑstoryboard : ์คํ ๋ฆฌ ๋ณด๋๋ฅผ ์์ ํ์ ๋