์ฌํ์ ์ฒซ ๋ฐ๊ฑธ์์ ๋ด๋๊ธฐ ์ ํ์ ์์๋ผ๊ณ ํ ์ ์๋ ์ธํด ๊ฒฝํ, ์ด๋ป๊ฒ ์ค๋นํด์ผํ ์ง ๋ง๋งํ๋ค๋ฉด?
์ธํด์ ์ฒ์ ์ค๋นํ๋ ๋ํ์์๊ฒ, ์ธํด์ ์ค๋นํ์ง๋ง ์ด๋ ค์์ ๊ฒช๊ณ ์๋ ๋ํ์์๊ฒ, ์ธํด์ ๊ฒฝํํ๊ฑฐ๋ ํ๊ณ ์๋ ๋ํ์์๊ฒ
๊ฐ์ฅ ๋จผ์ ์๊ฐ๋๋ ์๋น์ค๊ฐ ๋๊ธธ ๋ฐ๋ผ๋ฉฐ ์ ์ํ๊ฒ ๋์์ต๋๋ค.
- ๊ฐ๋ฐ ๊ธฐ๊ฐ : 2019๋ 12์ 21์ผ ~ 2020๋ 1์ 3์ผ
- ์ปค๋ฐ ๋ฉ์ธ์ง๋ ์์ด๋ก
- ๋ณ์ ๋ช ์ low camel ์ฌ์ฉ
- ๊ฐ์ ์ต์ ๋ ๊ธ์ง
- ๊ฐ์ ๋ถ๋ดํ ์คํ ๋ฆฌ๋ณด๋ ์ธ์ ๊ฑด๋ค๊ธฐ ๊ธ์ง
- ์ปค๋ฐ์ ๋ฐ๋์ ์๋ก์๊ฒ ์๋ฆด๊ฒ
- pod 'Alamofire', '~> 4.8.2'
- pod 'Kingfisher', '~> 4.10.1'
- pod 'CHIPageControl', '~> 0.1.3'
- pod 'Tabman', '~> 2.6'
- pod 'FSCalendar'
- 11.3
๊ธฐ๋ฅ | ๊ฐ๋ฐ ์ฌ๋ถ | ๊ธฐํ์ฌํญ | ๋ด๋น |
---|---|---|---|
์คํ๋์ฌ | โ | ์ฃผ์ฐ | |
์จ๋ณด๋ฉ | โ | ์ดํ ์ฒซ ์คํ ์ ๋์ | ๋ด |
๋ก๊ทธ์ธ | โ | ์ฃผ์ฐ,๋ด | |
ํ์๊ฐ์ (๊ธฐ๋ณธ์ ๋ณด) | โ | ์ด๋ฉ์ผ ์ค๋ณต ์ฒดํฌ โ | ๋ด |
ํ์๊ฐ์ (๊ด์ฌ์ง๋ฌด + ํ๋กํ ์ค์ ) | โณ | ์ฒซ ๋ฒ์งธ ๋ก๊ทธ์ธ ํ ๊ฒฝ์ฐ์๋ง ์ง์ | ๋ด |
ํ๋กํ ์ค์ | โณ | ์ฒซ ๋ฒ์งธ ๋ก๊ทธ์ธ ํ ๊ฒฝ์ฐ์๋ง ์ง์ | ๋ด |
ํ(๋ง์ถค ๊ณต๊ณ ) | โณ | ํ์ฌ url ์ฐ๊ฒฐ | ๋ด |
ํ(์ถ์ฒ ํ๋กํ) | โณ | ์์ธ ํ๋กํ ์ฐ๊ฒฐ | ๋ด |
ํ(์ค๋์ ์คํ ๋ฆฌ) | โณ | ์์ธ ์คํ ๋ฆฌ๋ทฐ ์ฐ๊ฒฐ | ๋ด |
๊ณต๊ณ | โ | ์ ์ฒด๊ณต๊ณ / ์ง๋ ๊ณต๊ณ ์์ผ๋ก ์ ๋ ฌ | ๋ด |
๊ณต๊ณ ์ถ๊ฐ | โ | ๊ด์ฌ์๋ ๊ณต๊ณ ๋ฅผ ์บ๋ฆฐ๋ ์ผ์ ์ ์ถ๊ฐ | ๋ด |
๊ณต๊ณ ํํฐ | โณ | ๋ณด๊ณ ์ถ์ ์ง๋ฌด๋ฅผ ํํฐ์ ๋ฐ๋ผ ์ค์ | ๋ด |
๊ณต๊ณ ์บ๋ฆฐ๋ | โ | ์๋ณ ๊ณต๊ณ ์กฐํ, ์ถ๊ฐํ ๊ณต๊ณ ๋ฆฌ์คํธ๋ฅผ ์บ๋ฆฐ๋์ ํ์ | ๋ด |
์คํ ๋ฆฌ | โ | ์ต์ ์ / ์กฐํ์ ์ ๋ ฌ | ๋ด |
์นดํ ๊ณ ๋ฆฌ ๋ณ ์คํ ๋ฆฌ ์กฐํ | ์ธํด / ๋์ธํ๋ / ๊ณต๋ชจ์ / ๋์๋ฆฌ / ์๊ฒฉ์ฆ / ๊ธฐํ ์ ๋ฐ๋ผ ์กฐํ | ๋ด | |
์คํ ๋ฆฌ ์์ธ๊ธ | โ | ๋ด | |
์คํ ๋ฆฌ ๋๊ธ | โ | ๋๊ธ๋ณด๊ธฐ + ๋๊ธ์ฐ๊ธฐ | ๋ด |
๊ฐ์ธ ํ๋กํ | โณ | ์ ๋ณด: ํ์๊ฐ์ ์์ ํ ์ค ์๊ฐ + ํ๋กํ ์ฌ์ง + ๊ด์ฌ ์ง๋ฌด | ์ฃผ์ฐ |
๊ฐ์ธ ํ๋กํ ์ชฝ์ง | โ | ๋ํ๋ฅผ ๋๋ ์ ์ ๋ฆฌ์คํธ | ์ฃผ์ฐ, ๋ด |
๊ฐ์ธ ํ๋กํ ์ชฝ์ง ์์ธ ๋ด์ฉ | โณ | ์ฃผ์ฐ | |
๊ฐ์ธ ํ๋กํ ํ์๋ผ์ธ ์กฐํ | โ | ์ฃผ์ฐ | |
๊ฐ์ธ ํ๋กํ ํ์๋ผ์ธ ์ถ๊ฐ | โณ | ํ๋ฌํ ๋ฒํผ์ ํตํด ํ์๋ผ์ธ ์ถ๊ฐ | ์ฃผ์ฐ |
ํ์๋ผ์ธ ์คํ ๋ฆฌ ๋ฆฌ์คํธ ์กฐํ | โ | ์ฃผ์ฐ, ๋ด | |
๊ฐ์ธ ํ๋กํ ์คํ ๋ฆฌ ์ถ๊ฐ | โณ | ์ฃผ์ฐ | |
๊ฐ์ธ ํ๋กํ ์คํ ๋ฆฌ ํ๋ก์ฐ ๋ฆฌ์คํธ | โ | ์ฃผ์ฐ, ๋ด |
- splash
- onBoarding
- Login & Signup
- FirstLogin
- home tab : ์ถ์ฒ ๊ณต๊ณ , ์ถ์ฒ ํ๋กํ, ์ต์ ์คํ ๋ฆฌ
- announcement tab
- ์ ์ฒด ๊ณต๊ณ , ์ง๋ ๊ณต๊ณ ์ ๋ ฌ ์ ํ
- ๊ณต๊ณ ํํฐ : ๋ณด๊ณ ์ถ์ ์ง๋ฌด 1๊ฐ ์ ํ, ํํฐ์ ๋ฐ๋ผ ์ ๋ ฌ
- ๊ณต๊ณ ์บ๋ฆฐ๋ : ๊ด์ฌ ์ถ๊ฐํ ๊ณต๊ณ ๋ฅผ ์บ๋ฆฐ๋์ ์ถ๊ฐ
- story tab
- ์คํ ๋ฆฌ ์ต์ ์, ์กฐํ์ ์ ๋ ฌ
- ํญ ๋ฐ : ์ธํด, ๋์ธํ๋, ๊ณต๋ชจ์ , ๋์๋ฆฌ, ์๊ฒฉ์ฆ, ๊ธฐํ์ ๋ฐ๋ฅธ ์คํ ๋ฆฌ ์กฐํ
- ์คํ ๋ฆฌ ์์ธ๊ธ
- ์คํ ๋ฆฌ ๋๊ธ
- profile tab
- ๊ฐ์ธ ํ๋กํ ์ ๋ณด ์กฐํ
- ๊ฐ์ธ ํ๋์ ๋ํ ํ์๋ผ์ธ ์์ฑ ๋ฐ ์กฐํ
- ๊ฐ์ธ ํ๋กํ > ํ์๋ผ์ธ > ๊ฐ ํ์๋ผ์ธ์ ๋ํด ์์ฑํ ์คํ ๋ฆฌ ๋ฆฌ์คํธ ์กฐํ
- ํ๋ก์ / ํ๋ก์ ์กฐํ
- ์ชฝ์ง ์ฃผ๊ณ ๋ฐ๊ธฐ
- ์๋ฒ์์ ๋ฐ์์จ ๋ฐ์ดํฐ๊ฐ decode ๋์ง ์์
-> ResponseString ์์ (https://app.quicktype.io ์ฐธ๊ณ )
enum CodingKeys: String, CodingKey {
case timelineIdx, userIdx, title
case start_date = "start_date"
case end_date = "end_date"
case category
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
timelineIdx = (try? values.decode(Int.self, forKey: .timelineIdx)) ?? 0
userIdx = (try? values.decode(Int.self, forKey: .userIdx)) ?? 0
title = (try? values.decode(String.self, forKey: .title)) ?? ""
start_date = (try? values.decode(String.self, forKey: .start_date)) ?? ""
end_date = (try? values.decode(String.self, forKey: .end_date)) ?? ""
category = (try? values.decode(String.self, forKey: .category)) ?? ""
}
- UIPicker + Tool Bar ์์ฑ -> ์ฝ๋๋ก ์์ฑ
// 1. ์ ์ฒด๋ฅผ ์ก๋ view ์์ฑ + constraint ๊ฑธ๊ธฐ
self.picker.isHidden = false
self.picker.frame = CGRect(x: 0, y: view.frame.height - 220, width: view.frame.width, height: 150)
view.addSubview(self.picker)
// 2. Tool Bar ์ ๋ค์ด๊ฐ ๋ฒํผ ์์ฑ
let btnDone = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.testfunc))
// 3. UIPicker Toolbar ์์ฑ + ์์ฑ setting
let barAccessory = UIToolbar(frame: CGRect(x: 0, y: 0, width: picker.frame.width, height: 44))
barAccessory.barStyle = UIBarStyle.default
barAccessory.isTranslucent = true
barAccessory.items = [btnDone]
picker.addSubview(barAccessory)
// 4. UIPickerView ์์ฑ
self.pickerView.frame = CGRect(x: 0, y: barAccessory.frame.height, width: view.frame.width, height: picker.frame.height - barAccessory.frame.height)
self.pickerView.delegate = self
self.pickerView.dataSource = self
self.pickerView.backgroundColor = UIColor.whiteFour
picker.addSubview(self.pickerView)
- CalendarView ๋ฅผ ๊ธฐํ์ ๋ง๊ฒ ์ปค์คํ ํ๊ธฐ
- FSCalendar Library ์ค์น ( https://rhammer.tistory.com/126 )
- DateFormatter ๋ฅผ ์ฌ์ฉํด ์ํ๋ ๋ ์ง์ ์ด๋ฒคํธ ์์ฑ
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "yyyy-MM-dd"
let xmas = formatter.date(from: "2019-12-25")
let sampledate = formatter.date(from: "2019-12-22")
dates = [xmas!, sampledate!]
extension CalendarViewController: FSCalendarDataSource{
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
if self.dates.contains(date){
return 1
}
return 0
}
}
- UITabBar ์ฌ์ฉํ๊ธฐ
- Tabman Library ์ค์น ( https://github.com/uias/Tabman )
- TMBarItem ๊ณต์ ๋ฌธ์ ์ฐธ์กฐํด custom
// Create bar
let bar = TMBar.ButtonBar()
bar.layout.transitionStyle = .snap
// Customize button color
bar.buttons.customize { (button) in
button.tintColor = UIColor.brownishGreyTwo
button.selectedTintColor = UIColor.marigold
}
bar.indicator.tintColor = UIColor.marigold
addBar(bar, dataSource: self, at: .top)
- Navigation Bar Line Custom
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if shadowImageView == nil {
shadowImageView = findShadowImage(under: navigationController!.navigationBar)
}
shadowImageView?.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
shadowImageView?.isHidden = false
}
private func findShadowImage(under view: UIView) -> UIImageView? {
if view is UIImageView && view.bounds.size.height <= 1 {
return (view as! UIImageView)
}
for subview in view.subviews {
if let imageView = findShadowImage(under: subview) {
return imageView
}
}
return nil
}
iOS ๊ฐ๋ฐ ๊ฒฝํ์ด ์์ด ์ํ์ฐฉ์ค๋ฅผ ๋ง์ด ๊ฒช์ผ๋ฉฐ ์ฑ์ฅํ๋ ์ค์ ๋๋ค. ์ธํด์ฆ ์ฒด๊ณ ์ฃผ์ฐ์ด ์ฒด์ฒด๊ณ
โผ๏ธ ๐
์ฑ๊ฐ๋ฐ์ ์ฒ์์ด๋ผ ๋ฅ์์น ๋ชปํ์ง๋ง ์ข์ ํ์ ๋๋ถ์ ๋ง์ด ์ฑ์ฅํ ์ ์๋ ๋ป๊น์ ์๊ฐ์ด์์ต๋๋ค! ์์์ด ์ฒด์ฒด๊ณ โฃ๏ธ