인터페이스 디자인 #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 |