본문 바로가기

프로젝트/Image Generator (w∕OpenAI)

01. 인터페이스 디자인 #1

인터페이스 디자인 #1


ContentView

간단한 기능을 하는 만큼 간단한 구성을 가진다.

  • NavigationView
  • toolbar
  • ImageView
  • TextView
  • TextField
  • Button

ContentView
| NavigationView & Toolbar

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {

            }
            .navigationTitle("Image Generator")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        //info
                    } label: {
                        Image(systemName: "info.circle")
                    }

                }

                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        //share
                    } label: {
                        Image(systemName: "square.and.arrow.up")
                    }

                }
            }
        }
    }
}

내부를 구성할 VStack을 하나 생성하고,
NavigationBar의 인터페이스를 작성한다.
좌측과 우측에 각각 앱의 정보를 확인할 수 있는 화면과 사진을 저장할 수 있는 기능을 추가할 예정이다.

ContentView
| ImageView

struct ContentView: View {
    @State var image: UIImage?

    var body: some View {
        NavigationView {
            VStack {
                Spacer()

                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 300, height: 300)
                } else {
                    Text("Type prompt to generatre image!")
                }

                Spacer()


            }
            .navigationTitle("Image Generator")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        //info
                    } label: {
                        Image(systemName: "info.circle")
                    }

                }

                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        //share
                    } label: {
                        Image(systemName: "square.and.arrow.up")
                    }

                }
            }
        }
    }
}

State 변수로 image를 선언해 이미지를 저장한다.
표시할 image가 존재한다면 ImageView를 통해 image를 표시하고,
그렇지 않다면 Text를 표시한다.
화면의 중앙에 존재할 수 있도록 위 아래로 Spacer를 추가했다.

ContentView
| TextField & Button

struct ContentView: View {
    @State var image: UIImage?
    @State var text = ""

    var body: some View {
        NavigationView {
            VStack {
                Spacer()

                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 300, height: 300)
                } else {
                    Text("Type prompt to generatre image!")
                }

                Spacer()

                TextField("prompt here...", text: $text)
                    .padding()

                Button {
                    //gen action here
                } label: {
                    Text("Generate")
                }
            }
            .padding()
            .navigationTitle("Image Generator")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        //info
                    } label: {
                        Image(systemName: "info.circle")
                    }

                }

                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        //share
                    } label: {
                        Image(systemName: "square.and.arrow.up")
                    }

                }
            }
        }
    }
}

Spacer의 아래로 TextField와 Button을 추가한다.
TextField에는 State 변수를 Binding으로 전달한다.
코드에서는 text 변수가 해당한다.

추가적으로 VStack에 Padding을 하나 추가해 TextField와 Button의 여백을 확보했다.

InfoView

struct InfoView: View {
    let blogAddr = URL(string: "https://chillog.page")
    let openAiAddr = URL(string: "https://openai.com")

    var body: some View {
        VStack {

        }
    }
}

앱의 기본 정보를 위해 두 개의 변수를 추가했다.
각각 OpenAI의 홈페이지 주소와 Blog 주소다.

화면을 구성하기 위한 이미지 두 개를 Asset에 추가했다.

struct InfoView: View {
    let blogAddr = URL(string: "https://chillog.page")
    let openAiAddr = URL(string: "https://openai.com")

    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text("Made by")
                        .font(.largeTitle)

                    Link("@Eggthem17", destination: blogAddr!)
                        .tint(.orange)
                }

                Spacer()

                Image("chillog.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .cornerRadius(15)
            }
            .frame(width: 240)
            .padding()

            Divider()
                .frame(minHeight: 2)
                .background(.black)

            HStack {

            }
        }
    }
}

VStack과 HStack을 사용한 간단한 UI를 구성했다.

'Made By'의 아래에는는 TextView가 아닌 Link를 추가해 터치하면 웹 브라우저로 이동될 수 있도록 구성했다.

struct InfoView: View {
    let blogAddr = URL(string: "https://chillog.page")
    let openAiAddr = URL(string: "https://openai.com")

    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text("Made by")
                        .font(.largeTitle)

                    Link("@Eggthem17", destination: blogAddr!)
                        .tint(.orange)
                }

                Spacer()

                Image("chillog.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .cornerRadius(15)
            }
            .frame(width: 240)
            .padding()

            Divider()
                .frame(minHeight: 2)
                .background(.white)

            HStack {
                VStack(alignment: .leading) {
                    Text("Powered by")
                        .font(.largeTitle)

                    Link("@OpenAI", destination: openAiAddr!)
                        .tint(.blue)
                }

                Spacer()

                Image("openai.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .cornerRadius(15)
            }
            .frame(width: 240)
            .padding()
        }
    }
}

같은 HStack을 하나 더 만들어 OpenAI의 정보도 표시한다.

문제는 OpenAI의 로고가 검은색이라 배경과 구분이 되지 않는다는 점이다.
테두리를 추가해 보자.

struct InfoView: View {
    let blogAddr = URL(string: "https://chillog.page")
    let openAiAddr = URL(string: "https://openai.com")

    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text("Made by")
                        .font(.largeTitle)

                    Link("@Eggthem17", destination: blogAddr!)
                        .tint(.orange)
                }

                Spacer()

                Image("chillog.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .cornerRadius(15)
            }
            .frame(width: 240)
            .padding()

            Divider()
                .frame(minHeight: 2)
                .background(.white)

            HStack {
                VStack(alignment: .leading) {
                    Text("Powered by")
                        .font(.largeTitle)

                    Link("@OpenAI", destination: openAiAddr!)
                        .tint(.blue)
                }

                Spacer()

                Image("openai.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .overlay {
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(.white, lineWidth: 3)
                    }
            }
            .frame(width: 240)
            .padding()
        }
    }
}

overlay를 사용해 RoundedRectangle을 하나 생성하고, stroke를 지정해 테투리를 만든다.

배경과 구분감이 생기니 블로그 로고와도 일체감이 생겨 조금 더 보기 좋아 보인다.

struct InfoView: View {
    let blogAddr = URL(string: "https://chillog.page")
    let openAiAddr = URL(string: "https://openai.com")

    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text("Made by")
                        .font(.largeTitle)

                    Link("@Eggthem17", destination: blogAddr!)
                        .tint(.orange)
                }

                Spacer()

                Image("chillog.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .cornerRadius(15)
            }
            .frame(width: 240)
            .padding()

            Divider()
                .frame(minHeight: 2)
                .background(.white)

            HStack {
                VStack(alignment: .leading) {
                    Text("Powered by")
                        .font(.largeTitle)

                    Link("@OpenAI", destination: openAiAddr!)
                        .tint(.blue)
                }

                Spacer()

                Image("openai.logo")
                    .resizable()
                    .frame(width: 60, height: 60)
                    .overlay {
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(.white, lineWidth: 3)
                    }
            }
            .frame(width: 240)
            .padding()
        }
        .frame(width: 240)
        .navigationBarTitleDisplayMode(.inline)
    }
}

마지막으로 Divider의 너비를 제한하기 위해 VStack에 너비를 지정해 주고,
화면의 Vertical 정렬을 위해 navigationBarTitleDisplayMode를 inline으로 변경한다.

InfoView
| ContentView & InfoView 연결

struct ContentView: View {
    @State var image: UIImage?
    @State var text = ""

    var body: some View {
        NavigationView {
            VStack {
                Spacer()

                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 300, height: 300)
                } else {
                    Text("Type prompt to generatre image!")
                }

                Spacer()

                TextField("prompt here...", text: $text)
                    .padding()

                Button {
                    //gen action here
                } label: {
                    Text("Generate")
                }
            }
            .padding()
            .navigationTitle("Image Generator")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    NavigationLink {
                        InfoView()
                    } label: {
                        Image(systemName: "info.circle")
                    }


                }

                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        //share
                    } label: {
                        Image(systemName: "square.and.arrow.up")
                    }

                }
            }
        }
    }
}

좌측 상단에 추가했던 Button 대신 NavigationLink를 추가한다.
해당 버튼은 이제 InfoView를 표시할 수 있다.

InfoView를 표시하고 Link를 사용해 페이지를 잘 보여준다.

'프로젝트 > Image Generator (w∕OpenAI)' 카테고리의 다른 글

05. 인터페이스 디자인 #2  (0) 2022.12.28
04. 기능구현 #3  (0) 2022.12.28
03. 기능구현 #2  (0) 2022.12.27
02. 기능 구현 #1  (0) 2022.12.27
00. 시작하며  (0) 2022.12.23