diff --git a/README.md b/README.md index 8594d4cf..140132cc 100755 --- a/README.md +++ b/README.md @@ -520,7 +520,7 @@ override func viewDidLayoutSubviews() { **Update skeleton** -You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods: +You can change the skeleton configuration at any time like its color, animation, etc. with the following methods: ```swift (1) view.updateSkeleton() // Solid diff --git a/Translations/README_ko.md b/Translations/README_ko.md index c63a30ff..e549ecc1 100644 --- a/Translations/README_ko.md +++ b/Translations/README_ko.md @@ -25,35 +25,29 @@ **๐ŸŒŽ ๋ฒˆ์—ญ์— ๋„์›€์„ ์ฃผ์‹ ๋ถ„๋“ค: [๐Ÿ‡ฌ๐Ÿ‡ง](../README.md) . [๐Ÿ‡จ๐Ÿ‡ณ](README_zh.md) . [๐Ÿ‡ง๐Ÿ‡ท](README_pt-br.md) . [๐Ÿ‡ฐ๐Ÿ‡ท](README_ko.md) . [๐Ÿ‡ซ๐Ÿ‡ท](README_fr.md) . [๐Ÿ‡ฉ๐Ÿ‡ช](README_de.md)** -์˜ค๋Š˜๋‚  ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„์˜ ์•ฑ๋“ค์€ ๋น„๋™๊ธฐ ๋ฐฉ์‹์˜ API ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. -ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž‘๋™ํ•˜๋Š”๋™์•ˆ ๊ฐœ๋ฐœ์ž๋“ค์€ ์ž‘์—…์ด ์‹คํ–‰๋˜๊ณ  ์žˆ๋‹ค๋Š”๊ฒƒ์„ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„œ ๋กœ๋”ฉ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. +์ตœ๊ทผ ์•ฑ๋“ค์€ ๋Œ€๋ถ€๋ถ„ ๋น„๋™๊ธฐ ๋ฐฉ์‹์˜ API ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. +ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋™์•ˆ ๊ฐœ๋ฐœ์ž๋“ค์€ ์ž‘์—…์ด ์‹คํ–‰๋˜๊ณ  ์žˆ๋‹ค๋Š”๊ฒƒ์„ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„œ ๋กœ๋”ฉ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ```SkeletonView```๋Š” ์ด๋Ÿฌํ•œ ํ•„์š”์— ์˜ํ•ด ๊ณ ์•ˆ๋˜์—ˆ๊ณ , ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ๋ฌด์—‡์ธ๊ฐ€ ๋กœ๋”ฉ์ด ๋˜๊ณ  ์žˆ๋‹ค๋Š”๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๋ฉด์„œ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ฝ˜ํ…์ธ ์— ๋Œ€ํ•ด์„œ๋„ ๋ฏธ๋ฆฌ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์šฐ์•„ํ•˜๊ฒŒ ํ‘œํ˜„ํ• ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค ๋ง˜๊ป ๋ˆ„๋ฆฌ์„ธ์š” ๐Ÿ™‚ -* [๊ธฐ๋Šฅ](#-features) -* [๊ฐ€์ด๋“œ](#-guides) -* [์„ค์น˜๋ฐฉ๋ฒ•](#-installation) - * [Cocoapods](#using-cocoapods) - * [Carthage](#using-carthage) - * [SPM](#using-swift-package-manager) -* [์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”?](#-how-to-use) - * [Collections](#-collections) - * [Multiline text](#-multiline-text) - * [Custom colors](#-custom-colors) - * [Appearance](#-appearance) - * [Custom animations](#-custom-animations) - * [Hierarchy](#-hierarchy) - * [Debug](#-debug) -* [๋ฌธ์„œํ™”](#-documentation) -* [์ง€์›๋˜๋Š” OS์™€ SDK ๋ฒ„์ „](#-supported-os--sdk-versions) -* [Next steps](#-next-steps) -* [Contributing](#-contributing) -* [Mentions](#-mentions) -* [๊ฐœ๋ฐœ์ž](#-author) -* [๋ผ์ด์„ผ์Šค](#-license) - +- [๐ŸŒŸ ๊ธฐ๋Šฅ](#-๊ธฐ๋Šฅ) +- [๐ŸŽฌ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ](#-์‚ฌ์šฉ-๊ฐ€์ด๋“œ) +- [๐Ÿ“ฒ ์„ค์น˜ ๋ฐฉ๋ฒ•](#-์„ค์น˜-๋ฐฉ๋ฒ•) +- [๐Ÿ’ ์‚ฌ์šฉ๋ฒ•](#-์‚ฌ์šฉ๋ฒ•) + - [๐ŸŒฟ Collections](#-collections) + - [๐Ÿ”  Text](#-text) + - [๐Ÿฆ‹ Appearance](#-appearance) + - [๐ŸŽจ Custom colors](#-custom-colors) + - [๐Ÿƒโ€โ™€๏ธ Animations](#๏ธ-animations) + - [๐Ÿ„ Transitions](#-transitions) +- [โœจ ๊ธฐํƒ€](#-๊ธฐํƒ€) +- [โค๏ธ ๊ธฐ์—ฌํ•˜๊ธฐ](#๏ธ-๊ธฐ์—ฌํ•˜๊ธฐ) +- [๐Ÿ“ข ์†Œ์‹](#-์†Œ์‹) +- [๐Ÿ† ์Šคํฐ์„œ](#-์Šคํฐ์„œ) +- [๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ๊ฐœ๋ฐœ์ž](#-๊ฐœ๋ฐœ์ž) +- [๐Ÿ‘ฎ๐Ÿป ๋ผ์ด์„ผ์Šค](#-๋ผ์ด์„ผ์Šค) ## ๐ŸŒŸ ๊ธฐ๋Šฅ @@ -65,9 +59,13 @@ - [x] ๊ฐ„๋‹จํ•œ ์Šค์œ„ํ”„ํŠธ ๋ฌธ๋ฒ• - [x] ๊ฐ€๋ณ๊ณ  ๊ฐ€๋…์„ฑ ์ข‹์€ ์ฝ”๋“œ -## ๐ŸŽฌ ์‚ฌ์šฉ๊ฐ€์ด๋“œ +## ๐ŸŽฌ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ [](https://youtu.be/75kgOhWsPNA) +| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/Zx1Pg1gPfxA/maxresdefault.jpg)](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) +|:---: | :---: | :---: | :---: +|[**SkeletonView ๊ฐ€์ด๋“œ - ์‹œ์ž‘ํ•˜๊ธฐ**](https://youtu.be/75kgOhWsPNA)|[**SkeletonView๋ฅผ ์‚ฌ์šฉํ•ด ๋กœ๋”ฉ ๋ทฐ ๋งŒ๋“ค๊ธฐ (Swift 5.2)**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**์•ฑ์—์„œ ์Šค์ผˆ๋ ˆํ†ค ๋กœ๋”ฉ ๋ทฐ ๋งŒ๋“ค๊ธฐ (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**iOS์—์„œ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋งŒ๋“ค๊ธฐ (์ŠคํŽ˜์ธ์–ด)**](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) by MoureDev + ## ๐Ÿ“ฒ ์„ค์น˜ ๋ฐฉ๋ฒ• @@ -99,9 +97,11 @@ github "Juanpe/SkeletonView" ] ``` +> ๐Ÿ“ฃ **์ค‘์š”!** +> +> ๋ฒ„์ „ 1.30.0 ๋ถ€ํ„ฐ, `SkeletonView`๊ฐ€ **XCFrameworks** ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. **XCFramework** ๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด [๋ ˆํฌ์ง€ํ† ๋ฆฌ](https://github.com/Juanpe/SkeletonView-XCFramework.git) ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”. - -## ๐Ÿ’ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”? +## ๐Ÿ’ ์‚ฌ์šฉ๋ฒ• `SkeletonView` ๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋”ฑ **3** ๋‹จ๊ณ„๋งŒ ๊ธฐ์–ตํ•˜์„ธ์š”: @@ -166,7 +166,6 @@ avatarImageView.isSkeletonable = true >>```SkeletonView``` ๋Š” ์žฌ๊ท€์ ์œผ๋กœ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค, ๋งŒ์•ฝ ๋ชจ๋“  ๋ทฐ์— ๋Œ€ํ•ด์„œ skeleton์„ ํ˜ธ์ถœํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ์—์„œ show `method`๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์ž๋ฉด UIViewControllers๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. - ### ๐ŸŒฟ Collections ํ˜„์žฌ, ```SkeletonView``` ๋Š” ```UITableView``` ์™€ ```UICollectionView```์—์„œ ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค. @@ -177,20 +176,17 @@ avatarImageView.isSkeletonable = true ``` swift public protocol SkeletonTableViewDataSource: UITableViewDataSource { - func numSections(in collectionSkeletonView: UITableView) -> Int + func numSections(in collectionSkeletonView: UITableView) -> Int // Default: 1 func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier + func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? // Default: nil + func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath) } ``` ํ•ด๋‹น ํ”„๋กœํ† ํด์€ ๋ณด์‹œ๋‹ค์‹œํ”ผ ```UITableViewDataSource```๋ฅผ ์ƒ์†๋ฐ›์•„ ๊ตฌํ˜„ํ•˜์˜€์œผ๋ฏ€๋กœ, skeleton์˜ protocol๊ณผ ๋Œ€์ฒด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. -ํ”„๋กœํ† ์ฝœ์˜ ๊ธฐ๋ณธ ๊ตฌํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: - -``` swift -func numSections(in collectionSkeletonView: UITableView) -> Int -// Default: 1 -``` +ํ”„๋กœํ† ์ฝœ์˜ ๊ธฐ๋ณธ ๊ตฌํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ฐ ์„น์…˜์— ๋ช‡ ๊ฐœ์˜ ์…€(=rows)์ด ๋“ค์–ด๊ฐ€๋Š”์ง€ ๋Ÿฐํƒ€์ž„์— ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. ``` swift func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int @@ -198,21 +194,56 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s // ์ „์ฒด ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ์ฑ„์šฐ๋Š”๋ฐ ํ•„์š”ํ•œ ์…€ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค ``` -ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋Š” ๋‹น์‹ ์ด ๊ตฌํ˜„ํ•˜์—ฌ์•ผํ•  cell identifier์„ ์•„๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค, ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋Š” ๊ธฐ๋ณธ์œผ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„๋ฉ๋‹ˆ๋‹ค : +> **์ค‘์š”!** +>> ์œ„ ๋ฉ”์†Œ๋“œ์—์„œ ```UITableView.automaticNumberOfSkeletonRows``` ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ ๊ฐ’(default)๊ณผ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. (์ฆ‰ ์ „์ฒด ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ์ฑ„์šฐ๋Š”๋ฐ ํ•„์š”ํ•œ ์…€ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค) + - ``` swift - func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier - ``` +Skeleton ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ cell identifier๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋ฉ”์†Œ๋“œ๋Š” 1๊ฐœ์ž…๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” ๊ธฐ๋ณธ ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค. **Example** - ``` swift +``` swift func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier { return "CellIdentifier" } - ``` +``` -> **์ค‘์š”!** -> ๋งŒ์•ฝ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ณ€ํ•˜๋Š” ์…€์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด (`tableView.rowHeight = UITableViewAutomaticDimension` ),`estimatedRowHeight`๋ฅผ ๋ฌด์กฐ๊ฑด ์ •์˜ํ•ด์ฃผ์„ธ์š”. +๊ธฐ๋ณธ์ ์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ฐ indexPath์—์„œ cell์„ dequeue ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ skeleton์ด ๋‚˜ํƒ€๋‚˜๊ธฐ ์ „์— ๋ณ€ํ™”๋ฅผ ์ฃผ๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. + +``` swift +func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? { + let cell = skeletonView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as? Cell + cell?.textField.isHidden = indexPath.row == 0 + return cell +} +``` + +deque ๋ถ€๋ถ„์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๊ฒŒ ๋งก๊ธฐ๋Š” ๊ฒฝ์šฐ, ์•„๋ž˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด cell์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +``` swift +func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath) { + let cell = cell as? Cell + cell?.textField.isHidden = indexPath.row == 0 +} +``` + +Tableview์˜ header์™€ footer์—๋„ skeleton์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ```SkeletonTableViewDelegate```๋ฅผ ์ฑ„ํƒํ•˜์„ธ์š”. + +``` swift +public protocol SkeletonTableViewDelegate: UITableViewDelegate { + func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil + func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil +} +``` + +> ๐Ÿ“ฃ **์ค‘์š”!** +> +> 1๏ธโƒฃ ๋งŒ์•ฝ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ณ€ํ•˜๋Š” ์…€์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด (`tableView.rowHeight = UITableViewAutomaticDimension` ),`estimatedRowHeight`๋ฅผ ๋ฌด์กฐ๊ฑด ์ •์˜ํ•ด์ฃผ์„ธ์š”. +> +> 2๏ธโƒฃ **`UITableViewCell`**์— ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๊ฒฝ์šฐ, cell์— ์ง์ ‘์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ **`contentView`** ์— ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +> ```swift +> self.contentView.addSubview(titleLabel) โœ… +> self.addSubview(titleLabel) โŒ +> ``` ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ **์–ด๋–ป๊ฒŒ ํŠน์ • ์š”์†Œ์— skeleton ์„ ์ง€์ •ํ• ๊นŒ์š”?** @@ -229,47 +260,109 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s ``` swift public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource { - func numSections(in collectionSkeletonView: UICollectionView) -> Int + func numSections(in collectionSkeletonView: UICollectionView) -> Int // default: 1 func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier + func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // default: nil + func collectionSkeletonView(_ skeletonView: UICollectionView, skeletonCellForItemAt indexPath: IndexPath) -> UICollectionViewCell? // default: nil + func collectionSkeletonView(_ skeletonView: UICollectionView, prepareCellForSkeleton cell: UICollectionViewCell, at indexPath: IndexPath) + func collectionSkeletonView(_ skeletonView: UICollectionView, prepareViewForSkeleton view: UICollectionReusableView, at indexPath: IndexPath) } ``` -```UITableView``` ์™€ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ๊ฐ™์Šต๋‹ˆ๋‹ค. +์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ```UITableView``` ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. -### ๐Ÿ“ฐ Multiline text +### ๐Ÿ”  Text ![](../Assets/multilines2.png) ํ…์ŠคํŠธ๊ฐ€ ๋“ค์–ด์žˆ๋Š” ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ```SkeletonView``` ์—์„œ ํ…์ŠคํŠธ์˜ ๋ผ์ธ์„ ๊ทธ๋ ค์ค๋‹ˆ๋‹ค. -๊ทธ๋ฆฌ๊ณ , ์›ํ•˜๋Š” ๋ผ์ธ ์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ```numberOfLines``` ์„ 0์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ์ž๋™์œผ๋กœ ํ•„์š”ํ•œ ๋ผ์ธ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ๊ทธ๋ ค์ค๋‹ˆ๋‹ค. ๋Œ€์‹  ๊ฐ’์ด ์„ค์ •๋˜์–ด์žˆ๋‹ค๋ฉด ์„ค์ •๋œ ์ˆ˜๋งŒํผ์˜ ๋ผ์ธ์ด ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค. -##### ๐ŸŽ› Customize +| ํ”„๋กœํผํ‹ฐ | ํƒ€์ž… | ๊ธฐ๋ณธ๊ฐ’ | Preview +| ------- | ------- |------- | ------- +| **lastLineFillPercent** | `CGFloat` | `70`| ![](../Assets/multiline_lastline.png) +| **linesCornerRadius** | `Int` | `0` | ![](../`Assets/multiline_corner.png) +| **skeletonLineSpacing** | `CGFloat` | `10` | ![](../Assets/multiline_lineSpacing.png) +| **skeletonPaddingInsets** | `UIEdgeInsets` | `.zero` | ![](../Assets/multiline_insets.png) +| **skeletonTextLineHeight** | `SkeletonTextLineHeight` | `.fixed(15)` | ![](../Assets/multiline_lineHeight.png) +| **skeletonTextNumberOfLines** | `SkeletonTextNumberOfLines` | `.inherited` | ![](../Assets/multiline_corner.png) -๋‹น์‹ ์€ ๋ฉ€ํ‹ฐ๋ผ์ธ์„ ์œ„ํ•ด ๋ช‡๊ฐ€์ง€ ์˜ต์…˜์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +์ฝ”๋“œ๋ฅผ ํ†ตํ•ด radius์˜ ๋น„์œจ์„ ์กฐ์ ˆํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•˜์„ธ์š”. +``` swift +descriptionTextView.lastLineFillPercent = 50 +descriptionTextView.linesCornerRadius = 5 +``` -| ์†์„ฑ | ๊ฐ’ | ๊ธฐ๋ณธ๊ฐ’ | ๋ฏธ๋ฆฌ๋ณด๊ธฐ | -| ----------------------------------------------- | --------- | ----- | ---------------------------------- | -| ๋งˆ์ง€๋ง‰ ๋ผ์ธ์˜ **ํผ์„ผํŠธ** ๋ฅผ ์ง€์ • ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. | `0...100` | `70%` | ![](../Assets/multiline_lastline.png) | -| ๋ผ์ธ์˜ **Corner radius** ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (**์ƒˆ๋กœ์šด๊ธฐ๋Šฅ**) | `0...10` | `0` | ![](../Assets/multiline_corner.png) | +**IB/Storyboard**๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ทธ๋ฆผ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. +![](../Assets/multiline_customize.png) +
-๋ผ์ธ์˜ radius๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” **์ฝ”๋“œ** ๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค, ์•„๋ž˜ ์ฒ˜๋Ÿผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค: -```swift -descriptionTextView.lastLineFillPercent = 50 -descriptionTextView.linesCornerRadius = 5 +**์–ด๋–ป๊ฒŒ ์›ํ•˜๋Š” ๋ผ์ธ ์ˆ˜๋ฅผ ์„ค์ •ํ• ๊นŒ์š”?** + +๊ธฐ๋ณธ์ ์œผ๋กœ ๋ผ์ธ ์ˆ˜๋Š” `numberofLines` ํ”„๋กœํผํ‹ฐ ๊ฐ’๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์›ํ•˜๋Š” ๋ผ์ธ ์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ `numberOfLines` ์„ 0์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ์ž๋™์œผ๋กœ ํ•„์š”ํ•œ ๋ผ์ธ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ๊ทธ๋ ค์ค๋‹ˆ๋‹ค. + +๋งŒ์•ฝ ํŠน์ • ๋ผ์ธ ์ˆ˜๋กœ ์„ค์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด `skeletonTextNumberOfLines`๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”„๋กœํผํ‹ฐ๋Š” 2๊ฐœ์˜ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. `inherited`๋Š” `numberOfLine` ๊ฐ’์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. `custom(Int)`๋Š” ์—ฐ๊ด€ ๊ฐ’(associated value)์œผ๋กœ ํŠน์ • ๋ผ์ธ ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด + +``` swift +label.skeletonTextNumberOfLines = 3 // .custom(3) ``` -ํ˜น์€ **IB/Storyboard** ๋ฅผ ์ด์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: +
-![](../Assets/multiline_customize.png) +> **โš ๏ธ ์‚ฌ๋ผ์ง ์ฃผ์˜!** +> +> **useFontLineHeight** ๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  **skeletonTextLineHeight**๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.: +> ```swift +> descriptionTextView.skeletonTextLineHeight = .relativeToFont +> ``` + +> **๐Ÿ“ฃ ์ค‘์š”!** +> +> ์—ฌ๋Ÿฌ ์ค„์ด ์•„๋‹Œ View์˜ ๊ฒฝ์šฐ , ํ•˜๋‚˜์˜ ์ค„์ด ๋งˆ์ง€๋ง‰ ์ค„๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. + +### ๐Ÿฆ‹ Appearance + +skeletons์€ ๊ธฐ๋ณธ appearance(=์™ธํ˜•)์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ์ƒ‰, ๊ทธ๋ผ๋””์–ธํŠธ, ์ค„ ์˜ต์…˜์„ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด, ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +๊ธฐ๋ณธ ๊ฐ’: +- **tintColor**: `UIColor` + - *default: `.skeletonDefault` (`.clouds`์™€ ๋™์ผํ•˜์ง€๋งŒ, ๋‹คํฌ๋ชจ๋“œ์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.)* +- **gradient**: SkeletonGradient + - *default: `SkeletonGradient(baseColor: .skeletonDefault)`* +- **multilineHeight**: `CGFloat` + - *default: 15* +- **multilineSpacing**: `CGFloat` + - *default: 10* +- **multilineLastLineFillPercent**: `Int` + - *default: 70* +- **multilineCornerRadius**: `Int` + - *default: 0* +- **skeletonCornerRadius**: `CGFloat` (IBInspectable) (์Šค์ผˆ๋ ˆํ†ค view ๋ชจ์„œ๋ฆฌ๋ฅผ ๋‘ฅ๊ธ€๊ฒŒ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”) + - *default: 0* + +`SkeletonAppearance.default`๋ฅผ ์‚ฌ์šฉํ•ด ์ด ๊ธฐ๋ณธ ๊ฐ’๋“ค์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ํ• ๋‹นํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +``` swift +SkeletonAppearance.default.multilineHeight = 20 +SkeletonAppearance.default.tintColor = .green +``` + +> **โš ๏ธ ์‚ฌ๋ผ์ง ์ฃผ์˜!** +> +> **useFontLineHeight** ๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  **textLineHeight**๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.: +> ```swift +> SkeletonAppearance.default.textLineHeight = .relativeToFont +> ``` ### ๐ŸŽจ Custom colors -๋‹น์‹ ์€ skeleton์˜ ์ƒ‰์ƒ์„ ์ง€์ • ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์›ํ•˜๋Š” ์ƒ‰์ƒ์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ฒจ์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. +skeleton์˜ ์ƒ‰์ƒ์„ ์ง€์ • ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ง€ ์›ํ•˜๋Š” ์ƒ‰์ƒ์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ฒจ์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. **๋‹จ์ƒ‰ ์ด์šฉ๋ฐฉ๋ฒ•** ``` swift @@ -277,6 +370,7 @@ view.showSkeleton(usingColor: UIColor.gray) // Solid // or view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0)) ``` + **๊ทธ๋ผ๋””์–ธํŠธ ์ด์šฉ ๋ฐฉ๋ฒ•** ``` swift let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue) @@ -288,39 +382,14 @@ view.showGradientSkeleton(usingGradient: gradient) // Gradient ```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...``` ![](../Assets/flatcolors.png) -###### ์œ„ ์ด๋ฏธ์ง€๋Š” [https://flatuicolors.com](https://flatuicolors.com) ์‚ฌ์ดํŠธ์—์„œ ๋ฐœ์ทŒํ–ˆ์Šต๋‹ˆ๋‹ค. - -### ๐Ÿฆ‹ Appearance - -**์ƒˆ๋กœ์šด ์‚ฌํ•ญ** skeleton ์€ ๊ธฐ๋ณธ์„ค์ • ๊ฐ’์ด ์ •ํ•ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ปค์Šคํ…€ ์ปฌ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด, `SkeletonView` ์— ์ง€์ • ๋˜์–ด์žˆ๋Š” ๊ธฐ๋ณธ์„ค์ •์„ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. -๊ธฐ๋ณธ ์„ค์ •๊ฐ’: -- **tintColor**: UIColor - - *๊ธฐ๋ณธ๊ฐ’: .clouds* -- **gradient**: SkeletonGradient - - *๊ธฐ๋ณธ๊ฐ’: SkeletonGradient(baseColor: .clouds)* -- **multilineHeight**: CGFloat - - *๊ธฐ๋ณธ๊ฐ’: 15* -- **multilineSpacing**: CGFloat - - *๊ธฐ๋ณธ๊ฐ’: 10* -- **multilineLastLineFillPercent**: Int - - *๊ธฐ๋ณธ๊ฐ’: 70* -- **multilineCornerRadius**: Int - - *๊ธฐ๋ณธ๊ฐ’: 0* - -`SkeletonAppearance.default` ์—๋Š” ์‚ฌ์šฉ ๋˜์–ด์ง€๋Š” ๊ธฐ๋ณธ ๊ฐ’๋“ค์ด ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค . ์•„๋ž˜์˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: -```Swift -SkeletonAppearance.default.multilineHeight = 20 -SkeletonAppearance.default.tintColor = .green -``` - - -### ๐Ÿค“ ์ปค์Šคํ…€ ์• ๋‹ˆ๋ฉ”์ด์…˜ +###### ์œ„ ์ด๋ฏธ์ง€๋Š” [https://flatuicolors.com](https://flatuicolors.com) ์‚ฌ์ดํŠธ์—์„œ ๋ฐœ์ทŒํ–ˆ์Šต๋‹ˆ๋‹ค. -```SkeletonView``` ์—๋Š” ๋‘๊ฐ€์ง€ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค, ๋‹จ์ƒ‰ *๋ฐ”์šด์Šค* ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๊ทธ๋ผ๋””์–ธํŠธ *์Šฌ๋ผ์ด๋“œ* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ž…๋‹ˆ๋‹ค . +### ๐Ÿƒโ€โ™€๏ธ Animations -๊ฒŒ๋‹ค๊ฐ€, ์ง์ ‘ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ •๋ง ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. +```SkeletonView``` ์—๋Š” ๋‘ ๊ฐ€์ง€ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค, ๋‹จ์ƒ‰ *๋ฐ”์šด์Šค* ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๊ทธ๋ผ๋””์–ธํŠธ *์Šฌ๋ผ์ด๋“œ* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ž…๋‹ˆ๋‹ค . +์ง์ ‘ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ •๋ง ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. Skeleton ์—์„œ๋Š” `showAnimatedSkeleton` ํ•จ์ˆ˜๋ฅผ ```SkeletonLayerAnimation```์— ์ •์˜ํ•˜์—ฌ ๋งž์ถคํ˜• ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. @@ -351,7 +420,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation) ``` -```GradientDirection``` ๋Š” enum ์œผ๋กœ ์ •์˜ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค., ์•„๋ž˜์˜ ์ผ€์ด์Šค๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”: +```GradientDirection``` ๋Š” enum ์œผ๋กœ ์ •์˜ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์ผ€์ด์Šค๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”: | ๋ฐฉํ–ฅ | ๋ฏธ๋ฆฌ๋ณด๊ธฐ | | ------------------- | ---------------------------------------------- | @@ -366,70 +435,166 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation) ์Šฌ๋ผ์ด๋”ฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋˜๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค, ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”: >>```let animation = GradientDirection.leftToRight.slidingAnimation()``` -### ๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๊ณ„์ธต ๊ตฌ์กฐ +### ๐Ÿ„ Transitions + +**SkeletonView** ์—๋Š” ๋ถ€๋“œ๋Ÿฝ๊ฒŒ **show** ํ•˜๊ฑฐ๋‚˜ **hide** ํ•˜๋Š” transition์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. -```SkeletonView```๋Š” ์žฌ๊ท€์ ์ž…๋‹ˆ๋‹ค , ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” skeleton์ด ํšจ์œจ์ ์œผ๋กœ ์ž‘๋™ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ์žฌ๊ท€์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ๋ฅผ `Skeletonable` ๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค, `skeletonable` ๋˜์ง€ ์•Š๋Š” ๋ทฐ๋ฅผ ๋งŒ๋‚˜๋Š” ์ˆœ๊ฐ„ ์žฌ๊ท€ ์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ธฐ ๋–„๋ฌธ์ž…๋‹ˆ๋‹ค. +Transition์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด, ```showSkeleton()```๋‚˜ ```hideSkeleton()``` ํ•จ์ˆ˜์— ```transition```ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. + +```swift +view.showSkeleton(transition: .crossDissolve(0.25)) //Show skeleton cross dissolve transition with 0.25 seconds fade time +view.hideSkeleton(transition: .crossDissolve(0.25)) //Hide skeleton cross dissolve transition with 0.25 seconds fade time + +``` + +๊ธฐ๋ณธ ๊ฐ’์€ `crossDissolve(0.25)` ์ž…๋‹ˆ๋‹ค. + +**Preview** + + + + + + + + + + +
+
None
+
+
Cross dissolve
+
+ + + +
+ + +## โœจ ๊ธฐํƒ€ + +**Hierarchy** + +```SkeletonView```๋Š” ์žฌ๊ท€์ ์ž…๋‹ˆ๋‹ค , ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” skeleton์ด ํšจ์œจ์ ์œผ๋กœ ์ž‘๋™ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ์žฌ๊ท€์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ์—์„œ, ๋ฐ˜๋“œ์‹œ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ๋ฅผ `Skeletonable` ๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. `skeletonable` ๋˜์ง€ ์•Š๋Š” ๋ทฐ๋ฅผ ๋งŒ๋‚˜๋Š” ์ˆœ๊ฐ„ ์žฌ๊ท€ ์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ธฐ ๋–„๋ฌธ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š” ์ด๋ฏธ์ง€๋Š” ํ•œ๋ˆˆ์— ์ดํ•ด๋˜์‹ค๊ฒ๋‹ˆ๋‹ค: > ```รฌsSkeletonable```= โ˜ ๏ธ -| ์„ค์ •๊ฐ’ | ๊ฒฐ๊ณผ | -| ----------------------------------------- | --------------------------------------------- | -| ![](../Assets/no_skeletonable.png) | ![](../Assets/no_skeletonables_result.png) | -| ![](../Assets/container_no_skeletonable.png) | ![](../Assets/no_skeletonables_result.png) | -| ![](../Assets/container_skeletonable.png) | ![](../Assets/container_skeletonable_result.png) | -| ![](../Assets/all_skeletonables.png) | ![](../Assets/all_skeletonables_result.png) | +| Configuration | Result| +|:-------:|:-------:| +| | | +| | | +| | | +|| | +| | | +| | | -### ๐Ÿ”ฌ ๋””๋ฒ„๊ทธ +**Skeleton views layout** -**์ƒˆ๋กœ์šด์†Œ์‹** ์–ด๋–ค๊ฒƒ๋“ค์ด ์ž˜ ๋™์ž‘ ํ•˜์ง€ ์•Š์„๋•Œ๋ฅผ ์œ„ํ•ด ๋””๋ฒ„๊ทธ ์ž‘์—…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ `SkeletonView` ์—๋Š” ๋ช‡๊ฐ€์ง€ ์ƒˆ๋กœ์šด ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. +๋ถ€๋ชจ ๋ทฐ bounds๊ฐ€ ๋ฐ”๋€œ์— ๋”ฐ๋ผ skeleton layout์ด view์˜ layout๊ณผ ์ž˜ ์•ˆ ๋งž์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +์ด๋Ÿฐ ๊ฒฝ์šฐ relayout์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -์ฒซ๋ฒˆ์จฐ๋กœ, `UIView` ์—์„œ skeleton ์ •๋ณด๋ฅผ ๋ณด๊ธฐ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: ```swift -var skeletonDescription: String +override func viewDidLayoutSubviews() { + view.layoutSkeletonIfNeeded() +} +``` + +> ๐Ÿ“ฃ **์ค‘์š”!** +> +> ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. 1.8.1 ๋ฒ„์ „๋ถ€ํ„ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. **์ˆ˜๋™์œผ๋กœ skeleton layout์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ**๋งŒ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +**Update Skeleton** + +skeleton ์ƒ‰, ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์–ธ์ œ๋“ ์ง€ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. + +```swift +(1) view.updateSkeleton() // Solid +(2) view.updateGradientSkeleton() // Gradient +(3) view.updateAnimatedSkeleton() // Solid animated +(4) view.updateAnimatedGradientSkeleton() // Gradient animated ``` -skeleton์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค: -![](../Assets/debug_description.png) +**์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘์‹œ view ์ˆจ๊ธฐ๊ธฐ** -๊ทธ๋ฆฌ๊ณ , ์ƒˆ๋กœ์šด **๋””๋ฒ„๊ทธ ๋ชจ๋“œ**๋ฅผ ํ™œ์„ฑํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ `SKELETON_DEBUG` ์ด๋ผ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด ํ™œ์„ฑํ™” ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. +์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์‹œ์ž‘๋  ๋•Œ view๋ฅผ ์ˆจ๊ธฐ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. -![](../Assets/debug_mode.png) +```swift +view.isHiddenWhenSkeletonIsActive = true // This works only when isSkeletonable = true +``` -๊ทธ๋Ÿฐ ์ดํ›„ skeleton์ด ๋‚˜์˜ค๋ฉด Xcode ์ฝ˜์†”์ฐฝ์—์„œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +**skeleton์ด ๋™์ž‘ ์ค‘์ผ ๋•Œ user interaction์„ ์ˆ˜์ •ํ•˜์ง€ ๋งˆ์„ธ์š”** + +๊ธฐ๋ณธ์ ์œผ๋กœ, skeleton๋œ ์•„์ดํ…œ๋“ค์— ๋Œ€ํ•ด user interacion์ด ๋น„ํ™œ์„ฑํ™” ๋ผ์žˆ์Šต๋‹ˆ๋‹ค. skeleton์ด ๋™์ž‘ ์ค‘์ผ ๋•Œ๋„ user interaction์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด `isUserInteractionDisabledWhenSkeletonIsActive ` ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”. + +```swift +view.isUserInteractionDisabledWhenSkeletonIsActive = false // The view will be active when the skeleton will be active. +``` + +**label์—์„œ skeleton ๋ผ์ธ ์„ค์ •์„ ์œ„ํ•ด font line height์„ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”** + +skeleton์ด `UILabel`๊ณผ `UITextView`์˜ font height์— ์ž๋™ ์กฐ์ ˆ๋˜๋Š” ๊ฒƒ์„ ๋น„ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ skeleton line height์€ text๋ฅผ ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด font height์— ์ž๋™ ์กฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. bounding box๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +```swift +label.useFontLineHeight = false +``` + +**skeleton ์ง€์—ฐํ•ด์„œ ๋ณด์—ฌ์ฃผ๊ธฐ** +view๊ฐ€ ๋นจ๋ฆฌ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๊ฒฝ์šฐ์— skeleton์ด ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์„ ์ง€์—ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```swift +func showSkeleton(usingColor: UIColor, + animated: Bool, + delay: TimeInterval, + transition: SkeletonTransitionStyle) +``` + +```swift +func showGradientSkeleton(usingGradient: SkeletonGradient, + animated: Bool, + delay: TimeInterval, + transition: SkeletonTransitionStyle) +``` -
-์˜ˆ์ œ๋ฅผ ํ™•์ธํ•ด๋ณด์„ธ์š”. - -
+**๋””๋ฒ„๊ทธ** +์–ด๋–ค ๊ฒƒ๋“ค์ด ์ž˜ ๋™์ž‘ ํ•˜์ง€ ์•Š์„ ๋•Œ, ๋””๋ฒ„๊ทธ ์ž‘์—…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ `SkeletonView` ์—๋Š” ๋ช‡๊ฐ€์ง€ ์ƒˆ๋กœ์šด ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. +์ฒซ ๋ฒˆ์งธ๋กœ, `UIView` ์—์„œ skeleton ์ •๋ณด๋ฅผ ๋ณด๊ธฐ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: +```swift +var skeletonDescription: String +``` + +๊ทธ๋ฆฌ๊ณ , ์ƒˆ๋กœ์šด **๋””๋ฒ„๊ทธ ๋ชจ๋“œ**๋ฅผ ํ™œ์„ฑํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ `SKELETON_DEBUG` ์ด๋ผ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด ํ™œ์„ฑํ™” ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. + +![](../Assets/debug_mode.png) + +๊ทธ๋Ÿฐ ์ดํ›„ skeleton์ด ๋‚˜์˜ค๋ฉด Xcode ์ฝ˜์†”์ฐฝ์—์„œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -### ๐Ÿ“š ๋ฌธ์„œํ™” -์กฐ๊ธˆ๋งŒ ๊ธฐ๋‹ค๋ ค์ฃผ์„ธ์š”...๐Ÿ˜… +``` +{ + "type" : "UIView", // UITableView, UILabel... + "isSkeletonable" : true, + "reference" : "0x000000014751ce30", + "children" : [ + { + "type" : "UIView", + "isSkeletonable" : true, + "children" : [ ... ], + "reference" : "0x000000014751cfa0" + } + ] +} +``` -### ๐Ÿ“‹ ์ง€์› ๊ฐ€๋Šฅํ•œ OS & SDK ๋ฒ„์ „ +**๐Ÿ“‹ ์ง€์› ๊ฐ€๋Šฅํ•œ OS & SDK ๋ฒ„์ „** * iOS 9.0+ * tvOS 9.0+ -* Swift 4.2 - -## ๐Ÿ“ฌ ์˜ˆ์ •๋œ ๊ธฐ๋Šฅ๋“ค - -* [x] ๋ฉ€ํ‹ฐ๋ผ์ธ ์—์„œ์˜ ๋งˆ์ง€๋ง‰ ๋ผ์ธ์˜ ์ฑ„์šฐ๊ธฐ ๋น„์œจ ์„ค์ • -* [x] ๋”๋งŽ์€ ๊ทธ๋ผ๋””์–ธํŠธ ์• ๋‹ˆ๋ฉ”์ด์…˜ -* [x] resizable cells ์ง€์› -* [x] CollectionView ํ˜ธํ™˜ -* [x] tvOS ํ˜ธํ™˜ -* [x] recovery state ์ถ”๊ฐ€ -* [x] Custom default appearance -* [x] ๋””๋ฒ„๊ทธ ๋ชจ๋“œ -* [ ] Custom collections ํ˜ธํ™˜ -* [ ] skeletons ๊ฐ€ ๋ณด์ด๊ฑฐ๋‚˜ ๊ฐ€๋ ค์งˆ๋•Œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€ -* [ ] MacOS ์™€ WatchOS ํ˜ธํ™˜ +* Swift 5.3 ## โค๏ธ ๊ธฐ์—ฌํ•˜๊ธฐ ์ด ํ”„๋กœ์ ํŠธ๋Š” ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ ์ž…๋‹ˆ๋‹ค, ๋งˆ์ŒํŽธํ•˜๊ฒŒ ๊ธฐ์—ฌํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค ์–ด๋–ป๊ฒŒ ํ•˜๋ƒ๊ตฌ์š”? @@ -439,9 +604,9 @@ skeleton์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค: ์ „์ฒด [๊ธฐ์—ฌ์ž๋ชฉ๋ก](https://github.com/Juanpe/SkeletonView/graphs/contributors) -###### [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค +์ž์„ธํ•œ ์ •๋ณด๋Š”, [contributing guidelines](https://github.com/Juanpe/SkeletonView/blob/main/CONTRIBUTING.md) ๋ฅผ ํ™•์ธํ•˜์„ธ์š”. -## ๐Ÿ“ข ์†Œ์‹๋“ค +## ๐Ÿ“ข ์†Œ์‹ - [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start) - [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful) @@ -455,17 +620,22 @@ skeleton์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค: - [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview) - [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74) - [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so) +- [Best iOS articles, new tools & more](https://medium.com/flawless-app-stories/best-ios-articles-new-tools-more-fcbe673e10d) + +## ๐Ÿ† ์Šคํฐ์„œ +์˜คํ”ˆ ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ๋„์›€์—†์ด ์ง€์†๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. SkeletonView๋ฅผ ์ž˜ ์‚ฌ์šฉํ•˜์…จ๋‹ค๋ฉด, ์Šคํฐ์„œ๊ฐ€ ๋˜์–ด ๋ณด๋Š” ๊ฒƒ์€ ์–ด๋– ์‹ ๊ฐ€์š”? +๋งํฌ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”. [GitHub Sponsors](https://github.com/sponsors/Juanpe) :heart: -## ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ๊ฐœ๋ฐœ์ž -[1.1]: http://i.imgur.com/tXSoThF.png -[1]: http://www.twitter.com/JuanpeCatalan -* Juanpe Catalรกn [![alt text][1.1]][1] +## ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ๊ฐœ๋ฐœ์ž + +[Juanpe Catalรกn](http://www.twitter.com/JuanpeCatalan) Buy me a coffee + ## ๐Ÿ‘ฎ๐Ÿป ๋ผ์ด์„ผ์Šค ```