역할에 따른 폴더 분류
프로젝트 생성 후 간편하게 보기 위해서 각각의 View 및 역할 별로 폴더를 Containers, Extentions, Modifier, Models, SupportingVIews, HomeView, Journal, Statistics, Resources 로 나누었다
Custom Modifier 만들기
Modifier은 font(), background() 같이 View에 여러 효과를 줄수 있다. View에 여러 modifier가 달려 있으면 보기 불편함으로 Custom Modifier을 만들어 해결한다. 이와 같이 ViewModifier Protocol을 구현해주면 된다.
import SwiftUI
struct FieldModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color.red)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}
Custom Modifier 적용하기
Modifier을 적용하기 위해서는 원하는 View에 다음과 같이 넣어 주면 된다.
Text("hi")
.modifier(FieldModifier())
Extension으로 기존 struct 확장
Extension
extension은 Swift의 문법으로 기존의 struct, class, enum 등에 새로운 기능을 추가 할 수 있다.
Modifier 쉽게 적용
modifier을 매번 위와 같이 쓰면 불편하다. 아래와 같이 View를 extension하면 쉽게 적용이 가능하다.
extension View {
func fieldStyle() -> some View {
self.modifier(FieldModifier())
}
}
Custom Color extension이용 줄여 사용
Custom Color는 아래와 같이 만들수 있다. 하지만 매번 적용하기 너무 번거롭다.
Color(red: 95/255, green: 77/255, blue: 147/255)
extension을 이용하면 간단하게 적용할 수 있다.
extension Color {
public static let back1 = Color(red: 95/255, green: 77/255,
blue: 147/255)
public static let back2 = Color(red: 227/255, green: 118/255,
blue: 130/255)
public static let field = Color(red: 241/255, green: 241/255,
blue: 241/255)
public static let barColor = Color(red: 85/255, green: 67/255,
blue: 137/255)
}
// Color.field 이런 형식으로 사용가능
Custom LinearGradient
LinearGradient는 그라데이션을 만들 수 있다.
extension LinearGradient {
public static let background = LinearGradient(gradient: Gradient(colors: [.back1, .back2]), startPoint: .topTrailing, endPoint: .bottomLeading)
}
Custom Button 만들기
기존의 SwiftUI 버튼은 파란색 글씨에 padding이 없는 단순한 형태이다. 이를 직사각형의 상자에 담긴 형태의 버튼으로 만들기 위해 SupportingViews폴더에 RectangularButton이라는 struct를 만들었다. title은 변화할 일이 없어서 상수 및 @State 없이 만들었고, isOn은 밖에서 넣어 준 Bool 값을 변화 시켜야 하므로 @Binding으로 선언하였다. 다음으로 body안에 Button을 만들었다. frame으로 가로의 길이를 고정시키고 위에서 선언한 modifier로 색을넣고 둥근모서리로 만든다.
import SwiftUI
struct RectangularButton: View {
let title: String
@Binding var isOn: Bool
var body: some View {
Button(action: {
self.isOn.toggle()
}){
Text(title)
.font(.headline)
}
.padding()
.frame(width: 300.0)
.foregroundColor(.black)
.fieldStyle()
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding(10)
}
}

HomeView: 기본 항목 배치
VStack을 이용해 View에 Image하나와 RectangularButton 3개를 배치한다. RectangularButton을 이용해 Bool 값을 변화시키고 .sheet가 변화를 감지하면 {}안의 View로 전환시킨다.
struct HomeView: View {
@State private var showingTodaySheet = false
@State private var showingManyTodaySheet = false
@State private var showingPastSheet = false
var body: some View {
VStack {
Image("default")
.resizable()
.scaledToFit()
.frame(width: 150, height: 150)
.padding(100)
RectangularButton(title: "오늘 마신 술 잔으로 추가하기",
isOn: self.$showingTodaySheet)
.sheet(isPresented: $showingTodaySheet){
Text("dd")
}
RectangularButton(title: "오늘 마신 술 여러 사람으로 나누기",
isOn: self.$showingManyTodaySheet)
.sheet(isPresented: $showingManyTodaySheet){
Text("ee")
}
RectangularButton(title: "과거에 마신 술 추가하기",
isOn: self.$showingPastSheet)
.sheet(isPresented: $showingPastSheet){
Text("ff")
}
}
}
}
HomeView: 배경색 넣기
배경색을 넣기 위해서는 ZStack을 이용한다 ZStack은 여러 항목을 배치하고 그 항목의 위치가 겹치도록 한다. 위에서 extension으로 만든LinearGradient.background를 이용해 그라데이션으로 배경을 넣었다.
struct HomeView: View {
@State private var showingTodaySheet = false
@State private var showingManyTodaySheet = false
@State private var showingPastSheet = false
var body: some View {
ZStack {
LinearGradient.background
.edgesIgnoringSafeArea(.all)
VStack {
//....앞의 코드 그대로
}
}
}
}
}

ContentView를 TabView로 만들기
rootView인 ContentView를 TabView로 만든다. .accentColor modifier은 선택했을때 색을 바꿔준다
struct ContentView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Image(systemName: "moon.fill")
Text("술 추가")
}
Text("임시")
.tabItem {
Image(systemName: "gear")
Text("설정")
}
}
.accentColor(.field)
}
}

TabBar에 색 넣기
init() 만들기
TabBar에 색을 넣기 위해서는 SwiftUI 코드로는 아직까지는 불가능하다. UIKit코드를 이용해야하는데 방법은 ContentView에 생성자를 넣고 다음과 같이 써주면된다.
init() {
let coloredAppearance = UITabBarAppearance()
coloredAppearance.backgroundColor = .clear
UITabBar.appearance().backgroundColor = .barColor
UITabBar.appearance().tintColor = .barColor
UITabBar.appearance().barTintColor = .barColor
UITabBar.appearance().unselectedItemTintColor = .gray
}
Modifier로 만들기
sturct안에 다음과 같은 코드가 있으면 불편하므로 Modfier를 만들고 Modfier를 사용하면 적용되도록 하자
struct TabBarModifier: ViewModifier {
init() {
let coloredAppearance = UITabBarAppearance()
coloredAppearance.backgroundColor = .clear
UITabBar.appearance().backgroundColor = .barColor
UITabBar.appearance().tintColor = .barColor
UITabBar.appearance().barTintColor = .barColor
UITabBar.appearance().unselectedItemTintColor = .gray
}
func body(content: Content) -> some View {
content
}
}
extension 적용하기
FieldModifier에 적용한 것처럼 View에 extension을 적용하도록 하자
extension View {
//...
func tabBarColor() -> some View {
self.modifier(TabBarModifier())
}
}
ContentView를 TabView로 만들기
ContentView에 다음과 같이 .tabBarColor를 추가한다.
struct ContentView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Image(systemName: "moon.fill")
Text("술 추가")
}
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("설정")
}
}
.accentColor(.field)
.tabBarColor()
}
}

RectangularNavigationLink 만들기
RectangularButton은 String과 Bool 값을 입력 받아 만들었다. 이 것을 RectangularNavigationLink는 View를 전달해야 하므로 다르게 만들어야 한다.
RectangularButton(title: "오늘 마신 술 여러 사람으로 나누기", isOn: $isOn)
먼저 Generic을 이용해 Lable, Destination을 선언하였다 where은 Generic에 대한 조건을 정의한것으로 View Protocol을 구현하여야 한다. @ViewBuilder 가 있어 label안에 여러개의 View를 배치할수 있다. @escaping이 있어 Closure로 만든 label이 외부에서 사용할 수 있다. body안에 modifier을 넣어 원하는 모양으로 바꾼다.
struct RectangularNavigationLink<Label, Destination>: View where
Label : View, Destination : View{
let label: () -> Label
let destination: Destination
init(destination: Destination, @ViewBuilder label: @escaping () -> Label) {
self.label = label
self.destination = destination
}
var body: some View {
NavigationLink(destination: destination, label: label)
.frame(height: 20)
.fieldStyle()
}
}

RectangularToggle 만들기
같은 방법으로 RectangularToggle을 만든다.
struct RectangularToggle<Label>: View where Label : View{
let label: () -> Label
var isOn: Binding<Bool>
init(isOn: Binding<Bool>, @ViewBuilder label: @escaping () -> Label) {
self.isOn = isOn
self.label = label
}
var body: some View {
Toggle(isOn: isOn, label: label)
.frame(height: 20)
.fieldStyle()
}
}

SectionTitle 만들기
SectionTitle은 RectangularButton을 만드는 방법대로 String을 받아 만든다.
struct SectionTitle: View {
let title: String
init(_ title: String) {
self.title = title
}
var body: some View {
Text(title)
.foregroundColor(.field)
.font(.headline)
}
}
구성요소 넣기
설정에 구성요소를 집어 넣는다.
struct SettingsView: View {
var body: some View {
NavigationView {
Form {
Section(header: SectionTitle("사용자 정보")) {
RectangularNavigationLink(destination: Text("ss")){
Text("사용자")
}
RectangularNavigationLink(destination: Text("ss")){
Text("친구 관리")
}
}
Section(header: SectionTitle("기본 설정")) {
RectangularToggle(isOn: .constant(true)){
Text("자동로그인")
}
RectangularToggle(isOn: .constant(true)){
Text("알림")
}
}
Section(header: SectionTitle("데이터 관리")) {
RectangularNavigationLink(destination: Text("ss")){
Text("사용자 음주량 추가")
}
RectangularNavigationLink(destination: Text("ss")){
Text("사용자 음주량 제거")
}
RectangularNavigationLink(destination: Text("ss")){
Text("술 표시 순서 변경")
}
RectangularNavigationLink(destination: Text("ss")){
Text("사용자 술 추가")
}
RectangularNavigationLink(destination: Text("ss")){
Text("단위 선택")
}
}
Section(header: SectionTitle("기타")) {
RectangularNavigationLink(destination: Text("ss")){
Text("도움말")
}
RectangularNavigationLink(destination: Text("ss")){
Text("회원탈퇴")
}
}
}
.navigationBarTitle(Text("설정"), displayMode: .inline)
}

TableViewModifier 만들기
위의 상태에서 배경을 넣어도 Table의 기본 색으로 인해 뒤에 배경이 보이지 않는다. 따라서 TableViewModifier을 만들어 색을 지워 보자. 만드는 방법은 TabBar의 Modifier를 만드는 것과 크게 다르지 않다.
struct TableVIewModifier: ViewModifier {
init(){
UITableView.appearance().backgroundColor = .clear
UITableViewCell.appearance().backgroundColor = .clear
UITableView.appearance().separatorColor = .clear
}
func body(content: Content) -> some View {
content
}
}
extension View {
//...
func tableClear() -> some View {
self.modifier(TableVIewModifier())
}
}
만든 modifier를 View에 추가 시킨다. 그리고 background modifer로 배경을 넣는다.
import SwiftUI
struct SettingsView: View {
var body: some View {
NavigationView {
Form {
//...
.tableClear()
}
.background(LinearGradient.background)
.navigationBarTitle(Text("설정"), displayMode: .inline)
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView()
}
}


NavigationBar에 색 넣기
NavigationBar에도 Modifer를 이용해 색을 넣어준다.
import SwiftUI
struct NavigationBarModifier: ViewModifier {
init( ) {
UINavigationBar.appearance().backgroundColor = .barColor
UINavigationBar.appearance().barTintColor = .barColor
UINavigationBar.appearance().tintColor = .field
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.field]
}
func body(content: Content) -> some View {
content
}
}
마찬가지로 View에 extension으로 modifier를 만들어 준다.
extension View {
func calendarDisplayMod(_ displayMode: FSCalendarScope) -> some View {
self.modifier(CalendarModifier(displayMode: displayMode))
}
}
import SwiftUI
struct SettingsView: View {
var body: some View {
NavigationView {
Form {
//...
.tableClear()
}
.background(LinearGradient.background)
.navigationBarTitle(Text("설정"), displayMode: .inline)
.navigationBarColor()
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView()
}
}
