원래의 뷰
struct CouplingView: View {
private let mycode = UserManager.shared.currentUserUID
@State private var clickPasteBtn = false
@Binding var isOpen : Bool
var body: some View {
NavigationView {
VStack{
ZStack {
HStack{
Button(action: {
isOpen = false
}){
Image(systemName: "xmark")
.resizable()
.foregroundColor(.tertiaryLabel)
.frame(width: UIScreen.getWidth(20),height: UIScreen.getHeight(20))
}
.frame(width: UIScreen.getWidth(20), height: UIScreen.getHeight(20))
.padding(.leading,UIScreen.getWidth(26))
Spacer()
}
HStack(alignment: .top) {
Spacer()
Text("연인연결하기")
.foregroundColor(.primaryLabel)
.font(Font.headlinefont())
Spacer()
}
}
.padding(.top,UIScreen.getHeight(20))
ZStack{
VStack {
Spacer()
Image(Assets.couplingpaper)
Text("연인에게 종이학 편지를 받으려면 아래 코드로 연결해주세요")
.foregroundColor(.primaryLabel)
.font(Font.bodyfont())
.multilineTextAlignment(.center)
.padding(.horizontal, UIScreen.getWidth(80))
.padding(.bottom, UIScreen.getHeight(40))
VStack(spacing: 30){
Text("나의 코드")
.foregroundColor(.secondaryLabel)
.font(Font.bodyfont())
Text(mycode)
.foregroundColor(.primaryLabel)
.font(Font.title3font())
.frame(width: UIScreen.getWidth(115),height: UIScreen.getHeight(15))
Button {
UIPasteboard.general.string = mycode
withAnimation(.easeInOut(duration: 0.2)) {
clickPasteBtn.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
withAnimation(.easeInOut){
clickPasteBtn = false
}
}
} label: {
Text("복사하기")
.foregroundColor(.lightPink)
.font(Font.bodyfont())
}
.padding(UIScreen.getHeight(10))
ShareLink(item: mycode, preview: SharePreview(
Text("사랑의 종이학")
)) {
Text("링크로 알려주기")
.foregroundColor(.gray1 )
.padding(.horizontal,UIScreen.getWidth(34))
.padding(.vertical, UIScreen.getHeight(13))
.background(Color.lightPink)
.cornerRadius(8)
.font(Font.bodyfont())
}
}
.padding(.vertical,40)
.frame(maxWidth: .infinity)
.background(Color.gray3)
.cornerRadius(18)
.padding(.horizontal,UIScreen.getWidth(54))
.padding(.bottom,UIScreen.getHeight(10))
Spacer()
NavigationLink(destination: InputCodeView(isopenfullscreen: $isOpen)) {
Text("상대방 코드 입력하기")
.foregroundColor(.lightPink)
.padding(.vertical,UIScreen.getHeight(18))
.frame(maxWidth: .infinity)
.font(Font.bodyfont())
}
.background(Color.gray3)
.cornerRadius(8)
.padding(.horizontal,UIScreen.getWidth(30))
.padding(.bottom,UIScreen.getHeight(10))
.overlay(
Group {
if clickPasteBtn {
ToastAlert(label: "코드가 복사 되었어요")
}
}
)
}
}
}.background(Color.backGround)
}
}}
위의 커플링뷰가 있다. 코드를 보면 body부분이 매우 길다. 이렇게 되면 협업에서 다른 사람이 내 코드를 이해하는데, 내가 코드를 다시 보는데도. 리팩하는데도 지장이 간다. 많은 오버헤드가 든다. 그러면 어떻게 저 긴 body를 가독성이 좋게 바꿔줄수 있을까?
1. struct로 만든다.
import SwiftUI
struct CouplingView: View {
// MARK: - Properties
private let myCode = UserManager.shared.currentUserUID
@State private var clickPasteBtn = false
@Binding var isOpen: Bool
// MARK: - Body
var body: some View {
NavigationView {
VStack {
// MARK: - Navigation Bar
NavigationBar(isOpen: $isOpen)
ZStack {
// MARK: - Header
Header()
// MARK: - Main Content
VStack(spacing: 30) {
Image(Assets.couplingpaper)
Text("연인에게 종이학 편지를 받으려면 아래 코드로 연결해주세요")
.foregroundColor(.primaryLabel)
.font(Font.bodyfont())
.multilineTextAlignment(.center)
.padding(.horizontal, UIScreen.getWidth(80))
.padding(.bottom, UIScreen.getHeight(40))
CodeSection(myCode: myCode, clickPasteBtn: $clickPasteBtn)
Spacer()
NavigationLink(destination: InputCodeView(isopenfullscreen: $isOpen)) {
Text("상대방 코드 입력하기")
.foregroundColor(.lightPink)
.padding(.vertical, UIScreen.getHeight(18))
.frame(maxWidth: .infinity)
.font(Font.bodyfont())
}
.background(Color.gray3)
.cornerRadius(8)
.padding(.horizontal, UIScreen.getWidth(30))
.padding(.bottom, UIScreen.getHeight(10))
.overlay(
Group {
if clickPasteBtn {
ToastAlert(label: "코드가 복사 되었어요")
}
}
)
}
.padding(.vertical, 40)
.frame(maxWidth: .infinity)
.background(Color.gray3)
.cornerRadius(18)
.padding(.horizontal, UIScreen.getWidth(54))
.padding(.bottom, UIScreen.getHeight(10))
}
}
.background(Color.backGround)
}
}
}
// MARK: - Subviews
extension CouplingView {
struct NavigationBar: View {
@Binding var isOpen: Bool
var body: some View {
ZStack {
HStack {
Button(action: {
isOpen = false
}) {
Image(systemName: "xmark")
.resizable()
.foregroundColor(.tertiaryLabel)
.frame(width: UIScreen.getWidth(20), height: UIScreen.getHeight(20))
}
.frame(width: UIScreen.getWidth(20), height: UIScreen.getHeight(20))
.padding(.leading, UIScreen.getWidth(26))
Spacer()
}
HStack(alignment: .top) {
Spacer()
Text("연인연결하기")
.foregroundColor(.primaryLabel)
.font(Font.headlinefont())
Spacer()
}
}
.padding(.top, UIScreen.getHeight(20))
}
}
struct Header: View {
var body: some View {
ZStack {
VStack {
Spacer()
Image(Assets.couplingpaper)
Text("연인에게 종이학 편지를 받으려면 아래 코드로 연결해주세요")
.foregroundColor(.primaryLabel)
.font(Font.bodyfont())
.multilineTextAlignment(.center)
.padding(.horizontal, UIScreen.getWidth(80))
.padding(.bottom, UIScreen.getHeight(40))
CodeSection(myCode: myCode, clickPasteBtn: $clickPasteBtn)
Spacer()
NavigationLink(destination: InputCodeView(isopenfullscreen: $isOpen)) {
Text("상대방 코드 입력하기")
.foregroundColor(.lightPink)
.padding(.vertical, UIScreen.getHeight(18))
.frame(maxWidth: .infinity)
.font(Font.bodyfont())
}
.background(Color.gray3)
.cornerRadius(8)
.padding(.horizontal, UIScreen.getWidth(30))
.padding(.bottom, UIScreen.getHeight(10))
.overlay(
Group {
if clickPasteBtn {
ToastAlert(label: "코드가 복사 되었어요")
}
}
)
}
.padding(.vertical, 40)
.frame(maxWidth: .infinity)
.background(Color.gray3)
.cornerRadius(18)
.padding(.horizontal, UIScreen.getWidth(54))
.padding(.bottom, UIScreen.getHeight(10))
}
}
}
struct CodeSection: View {
let myCode: String
@Binding var clickPasteBtn: Bool
var body: some View {
VStack(spacing: 30) {
Text("나의 코드")
.foregroundColor(.secondaryLabel)
.font(Font.bodyfont())
Text(myCode)
.foregroundColor(.primaryLabel)
.font(Font.title3font())
.frame(width: UIScreen.getWidth(115), height: UIScreen.getHeight(
struct view방식으로 객체화하여 분리하는 방법이다. 위의 방법으로 하면 가독성은 향상될수 있으나 변수들을 파라미터를 통해 전달해야하는 불편함이 생긴다.
그럼 이 방식은 언제 사용하는가?
한 뷰에서만 사용하는 것이 아닌 다른 뷰에서도 이 뷰를 사용해야할때 사용한다. 예를 들어 A, B, C view에서 확인Btn이 같다면 하나의 struct로 분리하고 재사용을 하는데 용이하다.
2. 메소드로 분리
import SwiftUI
struct CouplingView: View {
private let mycode = UserManager.shared.currentUserUID
@State private var clickPasteBtn = false
@Binding var isOpen : Bool
var body: some View {
NavigationView {
VStack{
ZStack {
HStack{
makecloseButton()
.frame(width: UIScreen.getWidth(20), height: UIScreen.getHeight(20))
.padding(.leading,UIScreen.getWidth(26))
Spacer()
}
HStack(alignment: .top) {
Spacer()
Text("연인연결하기")
.foregroundColor(.primaryLabel)
.font(Font.headlinefont())
Spacer()
}
}
.padding(.top,UIScreen.getHeight(20))
ZStack{
VStack {
Spacer()
Image(Assets.couplingpaper)
Text("연인에게 종이학 편지를 받으려면 아래 코드로 연결해주세요")
.foregroundColor(.primaryLabel)
.font(Font.bodyfont())
.multilineTextAlignment(.center)
.padding(.horizontal, UIScreen.getWidth(80))
.padding(.bottom, UIScreen.getHeight(40))
makecodeSharingView()
.padding(.horizontal,UIScreen.getWidth(54))
.padding(.bottom,UIScreen.getHeight(10))
Spacer()
makeinputPartnerCodeButton()
.padding(.horizontal,UIScreen.getWidth(30))
.padding(.bottom,UIScreen.getHeight(10))
.overlay(
Group {
if clickPasteBtn {
ToastAlert(label: "코드가 복사 되었어요")
}
}
)
}
}
}.background(Color.backGround)
}
}
@ViewBuilder
func makecloseButton() -> some View {
Button(action: {
isOpen = false
}){
Image(systemName: "xmark")
.resizable()
.foregroundColor(.tertiaryLabel)
.frame(width: UIScreen.getWidth(20),height: UIScreen.getHeight(20))
}
}
@ViewBuilder
func makecodeSharingView() -> some View {
VStack(spacing: 30){
Text("나의 코드")
.foregroundColor(.secondaryLabel)
.font(Font.bodyfont())
Text(mycode)
.foregroundColor(.primaryLabel)
.font(Font.title3font())
.frame(width: UIScreen.getWidth(115),height: UIScreen.getHeight(15))
Button {
UIPasteboard.general.string = mycode
withAnimation(.easeInOut(duration: 0.2)) {
clickPasteBtn.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
withAnimation(.easeInOut){
clickPasteBtn = false
}
}
} label: {
Text("복사하기")
.foregroundColor(.lightPink)
.font(Font.bodyfont())
}
.padding(UIScreen.getHeight(10))
ShareLink(item: mycode, preview: SharePreview(
Text("사랑의 종이학")
)) {
Text("링크로 알려주기")
.foregroundColor(.gray1 )
.padding(.horizontal,UIScreen.getWidth(34))
.padding(.vertical, UIScreen.getHeight(13))
.background(Color.lightPink)
.cornerRadius(8)
.font(Font.bodyfont())
}
}
.padding(.vertical,40)
.frame(maxWidth: .infinity)
.background(Color.gray3)
.cornerRadius(18)
}
@ViewBuilder
func makeinputPartnerCodeButton() -> some View {
NavigationLink(destination: InputCodeView(isopenfullscreen: $isOpen)) {
Text("상대방 코드 입력하기")
.foregroundColor(.lightPink)
.padding(.vertical,UIScreen.getHeight(18))
.frame(maxWidth: .infinity)
.font(Font.bodyfont())
}
.background(Color.gray3)
.cornerRadius(8)
}
}
1번에서는 struct로 분리를 하였더라면 이번에는 메소드로 분리를 하였다. 주의해야할 점은 메소드로 분리할때는 함수의 네이밍을 make나 build등 동사로 해주어야 기존의 컨벤션에 잘 맞는다. 그러면 이것은 언제쓸까???
이 뷰에서밖에 사용하지 않지만 한번만 그리는게 아닌 여러번 그려질때 사용된다. 근데 위에서 코드를 보면 단한번밖에 호출되지 않는다. 그렇다면 굳이ㅣ 이렇게 만들어 줄 필요가 있을까? nono
3. 연산프로퍼티를 통해
위에서 말한 문제점과 같이 한번밖에 사용하지 않을때는 func으로 만들게 되면 다른 사람이 코드를 볼때 의구심과 함께 혼란을 주어 헷갈리게 할수 있다. ㅠㅠㅠ 그럴때는 연산프로퍼티를 통해 뷰를 분리해주면 된다.
import SwiftUI
struct CouplingView: View {
private let mycode = UserManager.shared.currentUserUID
@State private var clickPasteBtn = false
@Binding var isOpen : Bool
var body: some View {
NavigationView {
VStack{
ZStack {
HStack{
closeButton
.frame(width: UIScreen.getWidth(20), height: UIScreen.getHeight(20))
.padding(.leading,UIScreen.getWidth(26))
Spacer()
}
HStack(alignment: .top) {
Spacer()
Text("연인연결하기")
.foregroundColor(.primaryLabel)
.font(Font.headlinefont())
Spacer()
}
}
.padding(.top,UIScreen.getHeight(20))
ZStack{
VStack {
Spacer()
Image(Assets.couplingpaper)
Text("연인에게 종이학 편지를 받으려면 아래 코드로 연결해주세요")
.foregroundColor(.primaryLabel)
.font(Font.bodyfont())
.multilineTextAlignment(.center)
.padding(.horizontal, UIScreen.getWidth(80))
.padding(.bottom, UIScreen.getHeight(40))
codeSharingView
.padding(.horizontal,UIScreen.getWidth(54))
.padding(.bottom,UIScreen.getHeight(10))
Spacer()
inputPartnerCodeButton
.padding(.horizontal,UIScreen.getWidth(30))
.padding(.bottom,UIScreen.getHeight(10))
.overlay(
Group {
if clickPasteBtn {
ToastAlert(label: "코드가 복사 되었어요")
}
}
)
}
}
}.background(Color.backGround)
}
}
var closeButton: some View{
Button(action: {
isOpen = false
}){
Image(systemName: "xmark")
.resizable()
.foregroundColor(.tertiaryLabel)
.frame(width: UIScreen.getWidth(20),height: UIScreen.getHeight(20))
}
}
var codeSharingView: some View {
VStack(spacing: 30){
Text("나의 코드")
.foregroundColor(.secondaryLabel)
.font(Font.bodyfont())
Text(mycode)
.foregroundColor(.primaryLabel)
.font(Font.title3font())
.frame(width: UIScreen.getWidth(115),height: UIScreen.getHeight(15))
Button {
UIPasteboard.general.string = mycode
withAnimation(.easeInOut(duration: 0.2)) {
clickPasteBtn.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
withAnimation(.easeInOut){
clickPasteBtn = false
}
}
} label: {
Text("복사하기")
.foregroundColor(.lightPink)
.font(Font.bodyfont())
}
.padding(UIScreen.getHeight(10))
ShareLink(item: mycode, preview: SharePreview(
Text("사랑의 종이학")
)) {
Text("링크로 알려주기")
.foregroundColor(.gray1 )
.padding(.horizontal,UIScreen.getWidth(34))
.padding(.vertical, UIScreen.getHeight(13))
.background(Color.lightPink)
.cornerRadius(8)
.font(Font.bodyfont())
}
}
.padding(.vertical,40)
.frame(maxWidth: .infinity)
.background(Color.gray3)
.cornerRadius(18)
}
var inputPartnerCodeButton: some View {
NavigationLink(destination: InputCodeView(isopenfullscreen: $isOpen)) {
Text("상대방 코드 입력하기")
.foregroundColor(.lightPink)
.padding(.vertical,UIScreen.getHeight(18))
.frame(maxWidth: .infinity)
.font(Font.bodyfont())
}
.background(Color.gray3)
.cornerRadius(8)
}
}
이런식으로 변수를 통해만들게되면 make등 동사로 네이밍을 안해줘도 된다.
@ViewBuilder는 저번에 배웠죠? 물론 여기서는 안써줘도 동작은 하지만 가독성을 위해 쓰는 것이 좋다.
정리를 하자면
1. 어떠한 뷰가 다른 뷰들에서 재사용될 확률이 있다? -> struct로 빼자
2. 다른뷰에서는 재사용되지 않지만 그 뷰안에서는 재사용된다? -> 메서드로 뷰를 만들자
3. 다른뷰 뿐만아니라 그 뷰안에서도 재사용 안된다? -> 연산프로퍼티로 뷰를 만들자
'SWIFTUI' 카테고리의 다른 글
View Modifier (0) | 2023.12.14 |
---|---|
근본으로 돌아가자 - View는 왜 구조체로 생성할까? (0) | 2023.12.14 |
ConfirmationDialog (0) | 2023.09.15 |
life cycle - swiftui (0) | 2023.07.03 |
weak, unowned (0) | 2023.05.29 |