SwiftUI 및 GraphQL을 사용한 Instagram 클론 – ProfileView

오늘은 GraphQL을 사용한 Instagram 복제 앱의 3부에서는 프로필 보기를 구축하는 SwiftUI에 대해 자세히 알아보겠습니다.

SwiftUI에서 구조체를 재사용하는 방법을 배우고 몇 가지 컨트롤에 대해 알아보겠습니다: VStack, HStack, GeometryReader, 텍스트, 버튼, 스페이서, 이미지, 디바이더 등 몇 가지 컨트롤에 대해 설명합니다.

이전 글에서는 Instagram 클론 앱의 각 UI를 사용하여 사용자를 등록하는 방법과 로그인하는 방법을 배웠습니다. 오늘은 훨씬 더 멋지게 만들어 보겠습니다.

안전벨트를 단단히 매고 시작해 보세요!

더 나은 학습을 위해 소스 코드가 포함된 iOS Instagram 클론 프로젝트를 다운로드하세요.

빠르게 시작하고 싶으신가요?

허브에서 이 앱을 복제하여 번거로움 없이 바로 사용해보세요!

정확히 무엇을 만들 것인가

우리는 다음과 같은 뷰를 만들 것입니다:

screen-shot-2019-09-16-at-10-08-35

SwiftUI를 사용하면 많은 구성 요소를 매우 쉽고 간단하게 재사용할 수 있기 때문에 복잡한 인터페이스를 정말 쉽게 구축할 수 있습니다.

다만 재사용 가능한 컴포넌트를 만드는 방법을 배워야 합니다. 하나의 뷰는 실제로는 여러 뷰의 조합으로 하나의 최종 결과물로 통합됩니다.
구성 요소의 간단한 목록은 다음과 같습니다:

screen-shot-2019-09-16-at-10-08-35

Where:

  • VStack
  • Hstack
  • 실제 컨트롤(버튼, 디바이더 등)

HStack, VStack, ZStack 뭐?

SwiftUI에서는 VStack(수직 스택)을 사용하여 뷰를 수직으로 정렬하고 HStack(수평 스택)을 사용하여 뷰를 수평으로 정렬하고 Zstack(z-index의 Z)으로 뷰를 오버레이하여 UI를 빌드합니다.

하나의 뷰가 다른 뷰 아래에 필요할 때마다 VStack을 사용해야 합니다.

하나의 뷰를 다른 뷰와 함께 사용해야 할 때마다 HStack을 사용해야 합니다.

하나의 보기가 다른 보기 위에 오버레이되어야 할 때마다 ZStack을 사용해야 합니다.

메인 뷰는 가로로 배치된 여러 뷰로 구성되므로 모든 것을 메인 VStack에 포함하고 거기서부터 시작하지만 전체 화면을 차지하기 위해 사용해야 할 것이 하나 더 있습니다: GeometryReader입니다.

이를 통해 모든 것을 (지오메트리리더의) 크기와 좌표의 함수로 렌더링할 수 있습니다.

재사용 가능성

재사용성은 SwiftUI의 장점 중 하나이므로 복잡한 컨트롤을 만들어 필요한 곳에 재사용할 수 있습니다.

이 앱에서는 주로 타임라인과 하단 표시줄이라는 두 가지 컴포넌트를 다른 보기에서 재사용할 것입니다:

screen-shot-2019-09-16-at-10-46-23

따라서 이러한 UI 코드를 별도로 생성하여 보다 체계적으로 정리할 것입니다. 뷰에 특정한 다른 모든 UI 코드는 해당 뷰의 코드 파일에 그대로 유지하면 됩니다.

그럼 시작해 보겠습니다…

프로필 뷰 만들기

새 SwiftUI 파일을 추가하고 이름을 ProfileView.swift로 지정합니다.

화면을 채우기 위해 컴포넌트를 정렬할 것이므로 내부의 모든 컨트롤이 크기와 좌표를 사용할 수 있도록 지오메트리 리더를 추가해 보겠습니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            
        }
    }
}

이제 우리의 뷰는 모든 컨트롤을 포함할 메인 VStack에 빌드될 것이므로 이 뷰도 추가해 보겠습니다.

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack{
                
            }
        }
    }
}

그리고 첫 번째 컨트롤 ‘라인’은 맨 위에 있는 컨트롤이 될 것입니다:

screen-shot-2019-09-16-at-10-53-00

단순한 한 줄의 컨트롤처럼 보이는 것은 실제로는 여러 개의 컨트롤입니다:

  • “사용자 이름” 텍스트
  • 그 옆에 아래를 가리키는 작은 화살표
  • 작은 화살표와 다음 컨트롤 사이의 공간을 채우는 스페이서(공백)
  • 오른쪽의 햄버거 버튼

버튼에는 아이콘을 사용할 것이므로 무료 아이콘을 찾아보세요. 이러한 아이콘을 찾기 좋은 곳은 Freepik입니다.

이제 HStack을 추가해 보겠습니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack{
                HStack{
                    
                }
            }
        }
    }
}

그리고 그 안에 첫 번째 텍스트가 있습니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack{
                HStack{
                    Text("username")
                }
            }
        }
    }
}

여기까지 읽어보셨을 겁니다:

screen-shot-2019-09-16-at-10-59-56

좋아요. 이제 시작입니다.

왼쪽에 정렬을 지정하는

(정렬: .leading)

코드를 추가합니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                }
            }
        }
    }
}

디자인에 일관성을 더하기 위해 Text 프로퍼티를 추가할 수도 있습니다:

AppDelegate에 정의된 lightBlueColor로 색상을 설정합니다:

.foregroundColor(lightBlueColor)

글꼴 무게를 변경하려면

.fontWeight(.semibold)

마지막으로 앞쪽에 약간의 패딩(공백)을 추가합니다:

.padding(.leading, 10)

이제 코드는 다음과 같이 보일 것입니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                    .fontWeight(.semibold)
                    .padding(.leading, 10)
                }
            }
        }
    }
}

이제 UI가 다음과 같이 보일 것입니다:

screen-shot-2019-09-16-at-11-07-31

이제 작은 화살표 버튼을 추가해 보겠습니다.

이 글의 2부에서 배운 대로 버튼의 아래쪽 화살표 이미지를 추가하고 SwiftUI 코드에 버튼을 추가해 보겠습니다:

Button(action: {}){
    
}

그리고 여기에 이미지를 추가합니다:

Button(action: {}){
    Image("화살표-다운")
    .resizable()
    .frame(너비: 10, 높이: 10)
}

그리고 앨링먼트를 위해 상단에 약간의 패딩을 추가해 보겠습니다:

Button(action: {}){
    Image("arrow-down")
    .resizable()
    .frame(너비: 10, 높이: 10)
}
.padding(.top, 5)

이제 전체 코드는 다음과 같아야 합니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                    .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                }
            }
        }
    }
}

그리고 UI도요:

screen-shot-2019-09-16-at-11-28-48

이제 햄버거 버튼을 같은 방식으로 추가해 보겠습니다:

Button(action: {}){
    Image("menu")
    .resizable()
    .frame(너비: 20, 높이: 20)
}.padding()

전체 코드는 다음과 같습니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                    .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                }
            }
        }
    }
}

그리고 우리의 뷰입니다:

screen-shot-2019-09-16-at-11-31-17

두 버튼 사이에 모든 공간을 차지하고 모든 것을 정렬할 수 있는 무언가가 있다면…

스페이서()

이제 보기 좋네요!

지금까지 작성한 전체 코드입니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                    .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    스페이서()
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                }
            }
        }
    }
}

이제 HStack의 높이를 수정하고 앞쪽에 약간의 패딩을 주면 준비가 완료됩니다:

.frame(높이: 50)
.padding(.leading, 10)

전체 코드입니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                    .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    스페이서()
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                }.frame(높이: 50)
                .padding(.leading, 10)
            }
        }
    }
}

이제 시작하겠습니다…

두 번째 HStack

기본 VStack 내부에 모든 것을 만들었으므로 첫 번째 HStack 외부에 추가하는 모든 새 컨트롤은 자동으로 그 아래에 배치됩니다.

이제 두 번째 HStack을 만들고 화면의 두 번째 부분을 만들 차례입니다:

screen-shot-2019-09-16-at-12-01-35

이 역시 하나의 HStack이 될 것이며, 그 안에는 이미지와 그 아래에 텍스트가 있는 숫자 3개와 4개의 VStack이 포함됩니다.

이제 개념을 이해하셨을 테니 이 새로운 HStack의 전체 코드를 보여드리겠습니다:

HStack{
    VStack{
        Image("logo-social")
        .resizable()
        .frame(너비: 90, 높이: 90)
            .clipShape(Circle())
            .shadow(반지름: 3)
            .overlay(Circle().stroke(Color.pink, lineWidth: 1))
        
        Text("Your Name")
            .foregroundColor(lightBlueColor)
            .fontWeight(.semibold)
    .padding(.leading, 10)
    
    VStack{
        Text("10")
        .font(.system(size: 30))
        .foregroundColor(lightBlueColor)
        .fontWeight(.bold)
        
        Text("출판물")
        .font(.system(size: 13))
        .foregroundColor(lightBlueColor)
    .padding(.leading, 30)
    
    VStack{
        Text("100")
        .font(.system(size: 30))
        .foregroundColor(lightBlueColor)
        .fontWeight(.bold)
        
        Text("팔로워")
        .font(.system(size: 13))
        .foregroundColor(lightBlueColor)
    }.padding()
    
    VStack{
        Text("1000")
        .font(.system(size: 30))
        .foregroundColor(lightBlueColor)
        .fontWeight(.bold)
        
        Text("Following")
        .font(.system(size: 13))
        .foregroundColor(lightBlueColor)
    }
    
}.frame(높이: 100)
.padding(.leading, 10)

전체 코드는 다음과 같습니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                        .fontWeight(.semibold)
                        .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    스페이서()
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                    
                }.frame(높이: 50)
                .padding(.leading, 10)
                
                HStack{
                    VStack{
                        이미지("로고-소셜")
                        .resizable()
                        .frame(너비: 90, 높이: 90)
                            .clipShape(Circle())
                            .shadow(반지름: 3)
                            .overlay(Circle().stroke(Color.pink, lineWidth: 1))
                        
                        Text("Your Name")
                            .foregroundColor(lightBlueColor)
                            .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    VStack{
                        Text("10")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("출판물")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    .padding(.leading, 30)
                    
                    VStack{
                        Text("100")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("팔로워")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }.padding()
                    
                    VStack{
                        Text("1000")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("Following")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }
                    
                }.frame(높이: 100)
                .padding(.leading, 10)
            }
        }
    }
}

이제 뷰는 다음과 같이 표시됩니다:

screen-shot-2019-09-16-at-12-06-17

깔끔하네요!

프로필 편집 버튼과 디바이더 추가하기

프로필 편집 버튼과 그 아래의 디바이더가 VStack에 있어야 한다고 생각하고 싶을 수 있습니다:

screen-shot-2019-09-16-at-13-29-27

하지만 전체 뷰가 기본 VStack 안에 있으므로 코드에 추가하기만 하면 되므로 실제로 그럴 필요는 없습니다:

 Button(action: {}){
    텍스트("프로필 편집")
    .fontWeight(.bold)
    .foregroundColor(lightBlueColor)
}.frame(width: 400)
.padding()

Divider()

를 사용하면 다음과 같이 보일 것입니다:

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리Reader { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                        .fontWeight(.semibold)
                        .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    스페이서()
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                    
                }.frame(높이: 50)
                .padding(.leading, 10)
                
                HStack{
                    VStack{
                        이미지("로고-소셜")
                        .resizable()
                        .frame(너비: 90, 높이: 90)
                            .clipShape(Circle())
                            .shadow(반지름: 3)
                            .overlay(Circle().stroke(Color.pink, lineWidth: 1))
                        
                        Text("Your Name")
                            .foregroundColor(lightBlueColor)
                            .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    VStack{
                        Text("10")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("출판물")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    .padding(.leading, 30)
                    
                    VStack{
                        Text("100")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("팔로워")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }.padding()
                    
                    VStack{
                        Text("1000")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("Following")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }
                    
                }.frame(높이: 100)
                .padding(.leading, 10)
                
                Button(action: {}){
                    텍스트("프로필 수정")
                    .fontWeight(.bold)
                    .foregroundColor(lightBlueColor)
                }.frame(width: 400)
                .padding()
                
                Divider()
            }
        }
    }
}

와 UI:

screen-shot-2019-09-16-at-13-34-15

타임라인 모방하기

타임라인 뷰는 애플리케이션의 다른 부분에서도 사용될 것이므로 다른 파일에서 분리하는 것이 좋습니다.
프로필 뷰 내부에서 똑같이 재사용할 수도 있지만 코드를 분리하면 더 체계적으로 정리할 수 있습니다.

TimelineView.swift 파일을 만듭니다.

SwiftUI에서 데이터를 표시하는 방법은 여러 가지가 있지만 제 앱에서는 이 방법을 선택했습니다:

  • 타임라인 뷰는 라인뷰의 VStack입니다.
  • 라인뷰는 3개의 프리뷰뷰로 구성된 HStack입니다.
  • 프리뷰뷰에는 이미지가 있습니다.

screen-shot-2019-09-16-at-13-29-27-copy-2

가장 먼저 할 일은 데이터를 저장할 구조체를 만드는 것입니다. 이 구조체를 Preview라고 부르며, 반복할 id(Int 유형)와 전달할 이미지의 URL을 저장할 imageURL(String 유형)의 두 가지 매개 변수를 갖습니다:

구조체 Preview {
    var id: Int
    let imageUrl: String
}

앞서 말했듯이 데이터를 표시하는 다른 방법을 선택할 수도 있지만, 저는 이것이 매우 이해하기 쉽다는 것을 알았으므로 먼저 PreviewView의 구조체를 추가해 보겠습니다.
구조체에는 나중에 설정할 Preview 프로퍼티가 있지만 이미지 렌더링에는 imageURL 프로퍼티를 사용합니다:

구조체 PreviewView: View {
    미리 보기: Preview
    
    var body: 일부 View {
        이미지(preview.imageUrl)
        .resizable()
        .frame(너비: 136, 높이: 136)
    }
}

이 작업이 완료되면 선에 표시할 3개의 미리보기 배열을 수신하는 LineView의 구조체를 추가할 수 있습니다.
나중에 실제 데이터를 반영하도록 변경할 예정이지만 지금은 괜찮을 것입니다:

구조체 LineView: View {
    let previewArray:[미리보기]
    
    var body: some View {
        HStack(spacing: 2){
            PreviewView(preview: previewArray[0])
            PreviewView(preview: previewArray[1])
            PreviewView(preview: previewArray[2])
        }
    }
}

마지막으로 반복할 미리보기 객체 배열을 만들 수 있습니다:

let previews:[Preview] = [
    Preview(id: 0, imageUrl: "1"),
    Preview(id: 1, imageUrl: "2"),
    Preview(id: 2, imageUrl: "3"),
    Preview(id: 3, imageUrl: "4"),
    Preview(id: 4, imageUrl: "5"),
    Preview(id: 5, imageUrl: "6"),
    Preview(id: 6, imageUrl: "7"),
    Preview(id: 7, imageUrl: "8"),
    Preview(id: 8, imageUrl: "9"),
    Preview(id: 9, imageUrl: "10"),
    Preview(id: 10, imageUrl: "11"),
    Preview(id: 11, imageUrl: "12"),
    Preview(id: 12, imageUrl: "13")
]

이 배열에는 13개의 객체가 있으며 사용할 이미지를 1부터 13까지의 이름으로 참조했습니다. 또한 해당 이미지를 자산 폴더에 저장했지만 나중에 다시 변경할 것입니다:

screen-shot-2019-09-16-at-14-06-55

이제 모든 준비가 완료되었으므로 배열을 반복하고 3개의 미리보기 객체를 전달하여 라인 프리뷰를 만들 수 있습니다.
동일한 객체를 전달하고 있지만, 다시 한 번 말하지만 이것은 표시를 위한 임시 객체이며 곧 변경될 것입니다:

var body: 일부 View {
        ScrollView{
            VStack(alignment: .leading, spacing: 2){
                ForEach(previews, id: \.id) { preview in
                    LineView(previewArray: [미리보기, 미리보기, 미리보기])
                }
            }
        }
}

전체 코드는 다음과 같습니다:

구조체 TimelineView: View {
    
    let previews:[미리보기] = [
        Preview(id: 0, imageUrl: "1"),
        Preview(id: 1, imageUrl: "2"),
        Preview(id: 2, imageUrl: "3"),
        Preview(id: 3, imageUrl: "4"),
        Preview(id: 4, imageUrl: "5"),
        Preview(id: 5, imageUrl: "6"),
        Preview(id: 6, imageUrl: "7"),
        Preview(id: 7, imageUrl: "8"),
        Preview(id: 8, imageUrl: "9"),
        Preview(id: 9, imageUrl: "10"),
        Preview(id: 10, imageUrl: "11"),
        Preview(id: 11, imageUrl: "12"),
        Preview(id: 12, imageUrl: "13")
    ]
    
    var body: 일부 View {
            ScrollView{
                VStack(alignment: .leading, spacing: 2){
                    ForEach(previews, id: \.id) { preview in
                        LineView(previewArray: [미리보기, 미리보기, 미리보기])
                    }
                }
            }
    }
}

구조체 타임라인뷰_프리뷰: PreviewProvider {
    static var previews: 일부 뷰 {
        TimelineView()
    }
}

구조체 미리보기 {
    var id: Int
    let imageUrl: String
}

구조체 LineView: View {
    let previewArray:[미리보기]
    
    var body: some View {
        HStack(spacing: 2){
            PreviewView(preview: previewArray[0])
            PreviewView(preview: previewArray[1])
            PreviewView(preview: previewArray[2])
        }
    }
}

구조체 PreviewView: View {
    
    let preview: Preview
    
    var body: 일부 View {
        이미지(preview.imageUrl)
        .resizable()
        .frame(너비: 136, 높이: 136)
    }
}

그리고 Divider 바로 아래의 ProfileView.swift에서 호출하면 됩니다:

...
 Button(action: {}){
    텍스트("프로필 편집")
    .fontWeight(.bold)
    .foregroundColor(lightBlueColor)
}.frame(width: 400)
.padding()

Divider()

TimelineView().padding(.leading, 10)

...

그 바로 아래에 다른 디바이더를 추가하여 원하는 최종 결과를 얻을 수도 있습니다:

...
 Button(action: {}){
    텍스트("프로필 편집")
    .fontWeight(.bold)
    .foregroundColor(lightBlueColor)
}.frame(width: 400)
.padding()

Divider()

TimelineView().padding(.leading, 10)

Divider()

...

어때 보여요?

screen-shot-2019-09-16-at-14-13-10

벌써 멋져 보이나요?

추가하여 마무리해 보겠습니다…

하단 보기

하단 뷰는 애플리케이션의 여러 부분에서 사용할 것이므로 또 다른 파일이 될 것입니다.

BottomView.swift 파일을 만들고 그 안에 스페이서가 있는 4개의 버튼으로 구성된 HStack(버튼이 나란히 배치될 예정이므로)을 만듭니다. 아이콘을 잊지 마세요!

구조체 BottomView: View {
    var body: 일부 View {
        HStack{
             Button(action: {}){
                 Image("home")
                 .resizable()
                 .frame(너비: 30, 높이: 30)
             }.padding()
             
             스페이서()
             
             Button(action: {}){
                 Image("search")
                 .resizable()
                 .frame(너비: 30, 높이: 30)
             }.padding()
             
             스페이서()
             
             Button(action: {}){
                 Image("plus-button")
                 .resizable()
                 .frame(너비: 30, 높이: 30)
             }.padding()
             
             스페이서()
             
             Button(action: {}){
                 Image("heart")
                 .resizable()
                 .frame(너비: 30, 높이: 30)
             }.padding()
             
             스페이서()
             
             Button(action: {}){
                 Image("user")
                 .resizable()
                 .frame(너비: 30, 높이: 30)
             }.padding()
        }.frame(height: 35)
    }
}

이것은 쉬웠습니다! 마지막 디바이더 바로 아래에 있는 ProfileView.swift에 통합해 봅시다:

...
Divider()
                
TimelineView().padding(.leading, 10)
                
Divider()
                
BottomView()
...

따라서 ProfileView의 전체 코드는 다음과 같습니다:

import SwiftUI

구조체 ProfileView: View {
    var body: 일부 View {
        지오메트리리더 { 지오메트리 in
            VStack(alignment: .leading){
                HStack{
                    Text("username")
                    .foregroundColor(lightBlueColor)
                        .fontWeight(.semibold)
                        .padding(.leading, 10)
                    
                    Button(action: {}){
                        Image("화살표-다운")
                        .resizable()
                        .frame(너비: 10, 높이: 10)
                    }
                    .padding(.top, 5)
                    
                    스페이서()
                    
                    Button(action: {}){
                        Image("menu")
                        .resizable()
                        .frame(너비: 20, 높이: 20)
                    }.padding()
                    
                }.frame(높이: 50)
                .padding(.leading, 10)
                
                HStack{
                    VStack{
                        이미지("로고-소셜")
                        .resizable()
                        .frame(너비: 90, 높이: 90)
                            .clipShape(Circle())
                            .shadow(반지름: 3)
                            .overlay(Circle().stroke(Color.pink, lineWidth: 1))
                        
                        Text("Your Name")
                            .foregroundColor(lightBlueColor)
                            .fontWeight(.semibold)
                    .padding(.leading, 10)
                    
                    VStack{
                        Text("10")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("출판물")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    .padding(.leading, 30)
                    
                    VStack{
                        Text("100")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("팔로워")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }.padding()
                    
                    VStack{
                        Text("1000")
                        .font(.system(size: 30))
                        .foregroundColor(lightBlueColor)
                        .fontWeight(.bold)
                        
                        Text("Following")
                        .font(.system(size: 13))
                        .foregroundColor(lightBlueColor)
                    }
                    
                }.frame(높이: 100)
                .padding(.leading, 10)
                
                Button(action: {}){
                    텍스트("프로필 수정")
                    .fontWeight(.bold)
                    .foregroundColor(lightBlueColor)
                }.frame(width: 400)
                .padding()
                
                Divider()
                
                TimelineView().padding(.leading, 10)
                
                Divider()
                
                BottomView()
            }
        }
    }
}

구조체 ProfileView_Previews: PreviewProvider {
    static var previews: 일부 View {
        ProfileView()
    }
}

 

그리고 마지막으로…

전체 프로필 뷰가 완성되었습니다:

screen-shot-2019-09-16-at-10-08-35

이제 얼마나 멋진가요!

결론

오늘은 앱에 프로필 보기를 모의 구현하는 방법을 배웠습니다. 아직은 모의에 불과하지만 시간이 지나면 몇 가지 기능을 추가할 것입니다.

SwiftUI에서 컴포넌트를 생성하고 재사용하는 방법과 이를 사용하여 복잡한 뷰를 만드는 방법을 배웠습니다. 멋지네요!

다음 글에서는 몇 가지 다른 뷰를 만들어 보겠습니다!

기대해주세요!

참고자료

지금 Back4app 가입하고 Instagram 클론 앱 제작을 시작하세요.

SwiftUI의 장점은 무엇인가요?

Instagram 앱과 같은 복제본을 만들고 싶다면 SwiftUI가 마법처럼 도와줄 것입니다.
복잡한 인터페이스 구축에 도움이 됩니다.
컴포넌트 재사용
인터페이스 재사용 프로세스가 간단하고 쉽습니다.
이러한 컴포넌트들을 함께 재사용하는 방법에 대한 지식만 있으면 됩니다.

Hstack, Vstack, Zstack은 무엇인가요?

이러한 이름은 미국식 표현 방식 때문에 서로 연결되어 있습니다. SwiftUI는 다음과 같은 패턴으로 UI를 빌드합니다.
UI는 수직 스택을 사용하여 수직으로 빌드됩니다.
UI는 수평 스택을 사용하여 수평으로 빌드됩니다.
겹치는 뷰는 ZStack을 사용하여 빌드됩니다.

SwiftUI의 가장 뛰어난 기능은 무엇입니까?

SwiftUI의 가장 큰 특징은 재사용성입니다. 컴포넌트를 반복해서 재사용하여 뷰를 빌드할 수 있습니다. 이는 모든 앱에 적용됩니다. 이러한 컴포넌트를 어떻게 다루는지 알아야 합니다.


Leave a reply

Your email address will not be published.