필터
컨테이너 내부의 값을 걸러서 추출하는 역할을 하는 매개변수로 함수를 갖는 함수 결과를 새로운 컨테이너에 담아 반환해준다
import Foundation
func evenFilter(number: Int) -> Bool {
return number % 2 == 0
}
let numbers: [Int] = [0, 1, 2, 3, 4 ,5]
let evenNumbers: [Int] = numbers.filter(evenFilter(number:))
클로저를 적용
클로저를 적용하여 다음과 같이 줄일 수 있다
let evenNumbers: [Int] = numbers.filter{ (number: Int) -> Bool in
return number % 2 == 0
}
클로저는 메소드에서 요구하는 형태로 같아야 전달인자로 전달할 수 있다 따라서 생략할 수 있다
let evenNumbers: [Int] = numbers.filter{ number -> Bool in
return number % 2 == 0
}
let evenNumbers: [Int] = numbers.filter{ number in
return number % 2 == 0
}
단축인자로 첫번째 전달인자부터 $0, $1, ... 순서로 사용할 수 있다
let evenNumbers: [Int] = numbers.filter{
return $0 % 2 == 0
}
클로저가 반환값을 갖는 클로저고 클로저 내부의 실행문이 단 한 줄이면 암시적으로 그 실행문을 반환 값으로 사용할 수 있다
let evenNumbers: [Int] = numbers.filter{
$0 % 2 == 0
}
리듀스
컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행하는 함수이다
public func reduce<Result>(_ initialResult: Result,
_ nextPartialResult: (Result, Element) throws -> Result)
rethrows -> Result
import Foundation
let numbers: [Int] = [1, 2, 3]
let sum: Int = numbers.reduce(0, { (result: Int, next : Int) -> Int in
return result + next
})
print(sum) //6
데이터 만들기
JSON 형식으로 다음 형식으로 데이터를 만들어 준다
[
{
"id": 1,
"date": "2020-01-01",
"amount": 52.564116539056215
},
{
"id": 2,
"date": "2020-01-02",
"amount": 70.78761513894055
},
{
"id": 3,
"date": "2020-01-03",
"amount": 63.97048579680779
},
{
"id": 4,
"date": "2020-01-04",
"amount": 34.93357242193006
},
//...
]
구조체 생성
데이터를 넣을 DTO를 만든다 Date를 출력하려면 새로운 변수를 선언에 DateFormatter를 이용해 String으로 변환해 주어야 한다
struct MyData: Codable, Identifiable {
let id: Int
let date: Date
let amount: Double
var formattedDate: String {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd"
return formatter.string(from: date)
}
}
디코더 만들기
Bundle을 extension하여 decode를 넣어준다
import SwiftUI
extension Bundle {
func decode(_ file: String) -> [MyData] {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("\\(file) 파일이 없음")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("\\(file) 불러오는데 실패")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode([MyData].self, from: data) else {
fatalError("\\(file) 디코드 실패")
}
return loaded
}
}
View에서 확인하기
View에서 Bundle.main.decode를 이용해 화면에 나오는 것을 확인 할 수 있다
import SwiftUI
struct ContentView: View {
let randoms: [MyData] = Bundle.main.decode("Data.json")
var body: some View {
List(randoms) { random in
Text("\\(random.amount)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

새로운 View 만들기
ChartsView라는 뷰를 만들고 MyData의 리스트를 변수로 선언한다
struct ChartsView: View {
var randoms: [MyData]
var body: some View {
Text("ddd")
}
}
차트 만들기
body안에 HStack을 이용해 여러개의 날짜별 amount를 표시하고 VStack을 이용해 막대와 날짜를 넣는다.
HStack(spacing: 1) {
ForEach(self.randoms) {random in
VStack {
Spacer()
Rectangle()
.fill(Color.purple)
.frame(width: 20 , height: CGFloat(random.amount))
Text(random.formattedDate)
.font(.caption)
.frame(width: 20, height: 40)
}
}
}
스크롤 기능 추가
ScrollView를 이용해 스크롤을 할수 있게 만들고 GeometryReader를 이용해 영역내에서 상대적인 크기로 값을 정할 수 있다
struct ChartsView: View {
var randoms: [MyData]
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: geometry.size.width/28) {
ForEach(self.randoms) {random in
VStack {
Spacer()
Rectangle()
.fill(Color.purple)
.frame(width: geometry.size.width/14 , height: geometry.size.height*(CGFloat(random.amount)/120))
Text(random.formattedDate)
.font(.caption)
.frame(width: geometry.size.width/14, height: 40)
}
}
}
}
}
}
}

정확한 양 표시
Spacer() 아래 다음을 넣는다 specifier를 이용해 소수점 둘째 자리까지 추가시키고 rotationEffect를 이용해 회전을 시키고 offset을 이용해 막대형 바와 겹치게 표현하였다
Text("\\(random.amount, specifier: "%.2f")")
.font(.footnote)
.rotationEffect(.degrees(-90))
.offset(y: 50)
.zIndex(1)
