๐ต [Update], [Create], [Fix] โก๏ธ ์ปค๋ฐ ๋ฉ์ธ์ง ํ์ ํต์ผ
๐ต ํ๊ธ๋ก ๋ณ๊ฒฝ๋ ํด๋๋ช , ๊ธฐ๋ฅ ๊ตฌํ ์ โก๏ธ ์ปค๋ฐ ๋ฉ์ธ์ง ํต์ผ
๐ต ViewController ==> ~~VC
๋ก ํ์ผ ํต์ผ
๐ต ๋ณ์๋ช
, ํจ์๋ช
Lower Camel
์ฌ์ฉ
๐ต Extension ์ด๋ฆ ==> ํ์ฅํด๋์ค + Extension
๐ต TableView
, CollectionView
์ Delegate, DataSource
class ViewController: UIViewController {}
extension ViewController: UITableViewDelegate {}
extension ViewController: UITableViewDataSource {}
๐ต Optional ๋ณ์ guard let
์ผ๋ก ๋ฐ์ธ๋ฉํ๊ธฐ
๐ต Xcode Version 11.3
๐ต Swift 5
โ Alamofire
โ KingFisher
โ CHIPageControl
โ XLPagerTabStrip
โ BEMCheckBox
โ Hero
์์ ํ๋ฉด
์์ํ๋ฉด ์ ๋๋ฉ์ด์
ํ์๊ฐ์
ํ์๊ฐ์ Progress Bar
์ทจํฅ ๋ถ์
์ทจํฅ ๋ถ์ ์ ๋๋ฉ์ด์
์ทจํฅ ๋ถ์ 2
ํ์ ํ๋ฉด
๋ฉ์ธ ํ๋ฉด
๋๋ฌ๋ณด๊ธฐ
ํํฐ ์ ๋๋ฉ์ด์
์์ธ ์ ๋ณด
์ฃผ๋ฌธํ๊ธฐ
์ฅ๋ฐ๊ตฌ๋
๊ตฌ๋งคํ๊ธฐ
๊ตฌ๋งคํ๊ธฐ ์ ๋๋ฉ์ด์
๊ฒฝ๋งค
๊ฒฝ๋งค ์ ๋๋ฉ์ด์
๋งค๊ฑฐ์ง
๋งค๊ฑฐ์ง ์ ๋๋ฉ์ด์
๋ง์ดํ์ด์ง
์ฐ ๋ชฉ๋ก
ํํญ
๊ธฐ๋ฅ | ๊ฐ๋ฐ์ฌ๋ถ | ๋ด๋น |
---|---|---|
๋ก๊ทธ์ธ/ํ์๊ฐ์ | O | ๋๋ฏผ |
ํํญ | O | ํ์ง |
๋๋ฌ๋ณด๊ธฐ | O | ๋๋ฏผ |
๊ฒฝ๋งค | โณ | ๋๋ฏผ |
๋งค๊ฑฐ์ง | O | ํ์ง |
๋ง์ดํ์ด์ง | โณ | ๋๋ฏผ |
์ฐ๋ชฉ๋ก | X | ๐ |
ํ๋ก์ฐ | X | ๐ |
์ธ๋ถ๊ธฐ๋ฅ
๊ธฐ๋ฅ | ๊ฐ๋ฐ์ฌ๋ถ | ๋ด๋น |
---|---|---|
SNS๋ก๊ทธ์ธ | X | ๐ง |
ํํญ ์ธ๋ถ์ ๋ณด | O | ํ์ง |
๋๋ฌ๋ณด๊ธฐ ํํฐ | โณ | ๋๋ฏผ |
์ฅ๋ฐ๊ตฌ๋ ๋ด๊ธฐ | O | ๋๋ฏผ |
์ฅ๋ฐ๊ตฌ๋ ๊ตฌ๋งคํ๊ธฐ | O | ๋๋ฏผ |
๊ฒฝ๋งค ์์ผํต์ | โณ | ๋๋ฏผ |
๋งค๊ฑฐ์ง ์ ๋๋ฉ์ด์ | O | ํ์ง |
์ทจํฅ ์ถ์ฒ | O | ๐จโ๐จโ๐งโ๐ง |
ํ๋ฝ ์ถ์ฒ | O | ๐จโ๐จโ๐งโ๐ง |
- CollectionView ๋ ์ด์์ ์ก๊ธฐ (DelegateFlowLayout ์์กํ)
๐ต Interface Builder AutoLayout ์ต์
์์ None
์ผ๋ก ์ง์ ํ๋ฉด ์ฝ๋๋ก ์ง์ ํ๋๋ก ์ค์
extension ViewController: UICollectionViewDelegateFlowLayout {
// ์, ํ, ์ข, ์ฐ Inset ์ง์
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 8, left: spacingWidth, bottom: 0, right: spacingWidth)
}
// ์ธ๋ก ๋ผ์ธ Spacing ์ง์
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
// ๊ฐ๋ก ๋ผ์ธ Spacing ์ง์
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
// ๊ฐ ์
Size ์ง์
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
}
- Animationable Property๊ฐ ์กด์ฌ
1๏ธโฃ frame
2๏ธโฃ bounds
3๏ธโฃ center
4๏ธโฃ transform
5๏ธโฃ alpha
6๏ธโฃ backgroundColor
7๏ธโฃ contentStretch
- ScrollView Delegateํ์ฉ PageControl ( โ
CHIPageControl
์ฌ์ฉ )
ํ์ด์ง ๋ฒํธ ๊ณ์ฐ
ํ์ฌ x ์์น๋ก View์ Width์ ๋๋์ด์ ํ์ฌ ํ์ด์ง ๊ณ์ฐ โก๏ธ Int ๊ฐ์ผ๋ก ํ์ฐ์ ํ๋ฉด ํ์ด์ง ๊ฐ์ด ๋์จ๋ค.
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let page = Int(targetContentOffset.pointee.x / self.view.frame.width)
pageControl.set(progress: page, animated: true)
}
- UIView Blur ์ฒ๋ฆฌ
var backgroundEffectView: UIVisualEffectView! // Visual Effect์ ๋ฃ์ ์ ์๋ View
func setBlurView() {
let blurEffect = UIBlurEffect(style: dark) // Blur Effect ๋ฃ์ ์ ์๋ ๊ฒ ์์ฑ
backgroundEffectView.effect = blurEffect // UIVisualEffectView์ Blurํจ๊ณผ ์ ์ฉ
}
- ์ ์ฒด ๋ ์ด์์์ด ์ ๋๋ก ์ ์ฉ ์๋๋ ๋ฌธ์ iPhone 11 Pro Max ์์ ์์ ํ ==> iPhone 11 Pro์ ์ ์ฉํ ๊ฒฝ์ฐ ๋ ์ด์์์ด Pro Max๋ก ์ ์ฉ๋์ด Button์ Radius๊ฐ ์ ์ฉ์ด ์๋จ
// Layout ๊ด๋ จ ๋ฉ์๋ ๊ณต๋ถ๋ค ๋ ํ์
// View์ SubView๋ค์ ๋ ์ด์์ ๊ด๋ จํด์ ๋ค์ ์ก์์ค ํ์๊ฐ ์์ ๊ฒฝ์ฐ
// ์ด ํจ์์์ View๋ค์ Layout์ ๋ค์ ๋ฆฌ์
์์ผ์ฃผ๋ฉด์ ์ก์์ค๋ค.
// iPhone11 Pro Max๋ฅผ ๊ธฐ์ค์ผ๋ก ์กํ์๋ ๊ฒ์ iPhone 11 Pro์ ๊ธฐ์ค์ผ๋ก ๋ค์ ์ก์ ์ ์๊ฒ ํด์ค๋ค.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
initColorButtons()
}
- StackView ์ด์ฉ Animation ์ ์ฉ AutoLayout ์ก์์ฃผ๊ธฐ
๐ต ํํฐ๋ฅผ ์ ์ฉํ์ ๋๋ง ์ ํํ๋ ๋ฉ๋ด์์ StackView์ ํ์ฉ ์คํ ๋ ์ด์์์ ์ก์์ฃผ์๋ค. isHidden
์ต์
ํ์ฉ
detailFilterView.isHidden = false
// setNeedsLayout ํ์ isHidden ํ ==> ์ด ๋ถ๋ถ๋ ๊ณต๋ถ ํ์
self.view.setNeedsLayout()
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
// ๋ณดํต isHidden ํ ๋ํ๋ ๋, ํ๋ฉด ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ๋ ๋ฌธ์ ๋ก layoutIfNeeded() ํ์
// ์ฌ๊ธฐ๋ ๊ณต๋ถ ํ์
}, completion: nil)
- ๊ฐ์ฅ ์์ View๋ก ๋์ฐ๊ธฐ (Navigation Bar, Tab Bar ์๋ก View๊ฐ ์ฌ๋ผ์ด)
guard let window = UIApplication.shared.keyWindow else { return }
window.addSubView(UIView())
- Tabbar
isHidden
์ Bottom์ Spacing์ด ์๊ธฐ๋ ๋ฌธ์
// hideBottomBarWhenPushed์ ๋์ด๊ฐ๋ View์ ์ค์
private func setTabbar() {
self.hidesBottomBarWhenPushed = true
}
- ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋, ์๋ฒ์์ Key๊ฐ ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐ
๐ต JSON
์์ ํญ์ Key๊ฐ์ String์ด๋๊น CodingKeys๋ผ๋ enum์ CodingKey๋ผ๋ ํ๋กํ ์ฝ์ ์ฑํํ๊ฒ ํ๋ค.
๐ต JSON
ํ์
์์ name์ ํค๊ฐ ์์ ๊ฒฝ์ฐ โก๏ธ myName์ผ๋ก ๋์
๐ต JSON
ํ์
์์ age์ ํค๊ฐ ์์ ๊ฒฝ์ฐ โก๏ธ myAge์ผ๋ก ๋์
struct Person: Codable {
var myName: String
var myAge: Int?
enum CodingKeys: String, CodingKey {
case myName = "name"
case myAge = "age"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
myName = (try? values.decode(String.self, forkey: .myName)) ?? ""
myAge = (try? values.decode(String.self, forkey: .myAge)) ?? nil
}
}
Dictionary
ํ์ Value๊ฐ์ผ๋ก Sortingํ๊ธฐ
// key, value ์์ผ๋ก Sorting ==> value์ ๊ฐ์ ๊ธฐ์ค์ผ๋ก ํฐ ๊ฐ์ด ์ผ์ชฝ์ ์ค๊ฒ ๋ฐ๊พธ๊ธฐ
let sortedParameter = surveyResult.sorted { $0.1 > $1.1 }
let sortingKey = [sortedParameter[0].key, sortedParameter[1].key, sortedParameter[2].key]
- ํค๋ณด๋๊ฐ View ํฐ์น Keyboard Down Event ๋ฐ์
self.view.endEditting(true)
- NavigationBar์ BarButtonItem ์ฝ๋๋ก ์ถ๊ฐํ๊ธฐ
private func setNaviBackButton() {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "10"), style: .done, target: self, action: #selector(popView))
self.navigationController?.navigationBar.topItem?.title = ""
}
@objc func popView() {
self.navigationController?.popViewController(animated: true)
}
TableView
์ ํ ํ, ๋ค์๋ทฐ๋ก ๋์ด๊ฐ๋ค ์ฌ ๋ ์ ํ๋ ์์ญ ํด์ ํ๊ธฐ
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if let index = myPageButtonTableView.indexPathForSelectedRow {
myPageButtonTableView.deselectRow(at: index, animated: true)
}
}
TableView
๋ฐ์ค ๋ผ์ธ์ด ๋น๋ ํ์ ํด๊ฒฐ โก๏ธ ๋น๋ ๊ณต๊ฐ ์๊ฒ
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cartCell = tableView.dequeueReusableCell(withIdentifier: "cartCell") as? CartTableViewCell else { return UITableViewCell() }
// zero๋ก ์ฃผ๋ฉด ๋ฌธ์ ํด๊ฒฐ ์ข๊ณต๊ฐ Inset์ด 0์ผ๋ก ์ค์ ๋๋ค.
cartCell.separatorInset = UIEdgeInsets.zero
}
- ์นด๋ฉ๋ผ ์ฌ์ฉํ๊ธฐ
UIImagePicker
์ฌ์ฉ
โ Info.plist Camera, Galary ์ ๊ทผ ๊ถํ ์ค์ ํ์, UIImagePickeroControllerDelegate ์ค์ ํ์
extension FluvApplyVC: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func openLibrary() {
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: false, completion: nil)
}
func openCamera() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePicker.sourceType = .camera
} else {
}
present(imagePicker, animated: false, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
profileImage = image
profileButton.setBackgroundImage(profileImage, for: .normal)
dismiss(animated: true, completion: nil)
}
}
}
NumberFormatter
์ฌ์ฉ ์ซ์ ๊ตฌ๋ถ
// 3์๋ฆฌ๋ถํฐ ๋จ์ ์ฌ์ฉํ๊ฒ ๊ตฌ๋ถ
let numberFormatter = NumberFormatter()
numberFormatter.style = .decimal
let text = numberFormatter.string(from: NSNumber(value: styleData[indexPath.row].price))! + "์"