[SwiftUI] 인스타그램 클론 어게인 View part

Woozoo·2023년 3월 20일
0

개인프로젝트

목록 보기
8/12

탭뷰 추가

탭뷰 추가하기 전에
폴더 정리한다고 파일 옮겨 줬는데
캔버스 맛탱이가 가버림...
종종 겪는 거 같은데 해결 어떻게 했더라


앱 경로 재설정 해주니까 돌아옴


탭뷰안에 뷰, 그리고 이 뷰들에 .tabItem 만들면 탭뷰가 되는거죠~

칼라 extension 만들어주고

FeedView & PostView

탭 중 하나인 FeedView를 스크롤이 되게 만들어주고
SubView로 PostView를 구성해줬다!

PostModel


요런 정보들이 필요하겠죠

이 모델을 사용해서 PostView에서 데이터 갈아낄 수 있게 해줍시다

그리고 PostArray가 담기게 될 Object를 하나 만들어줌
나중에 DataBase를 받아올 거라 지금처럼 init될 때 append 되는 느낌으로 만들어줬다

class PostArrayObject: ObservableObject {
    @Published var dataArray = [PostModel]()
    
    init() {
        print("Fetch from DataBase Here")
        let post1 = PostModel(postID: "", userID: "", username: "Woozoobro", caption: "This is a caption", dateCreated: Date(), likeCount: 0, likedByUser: false)
        let post2 = PostModel(postID: "", userID: "", username: "Jessica", caption: nil,  dateCreated: Date(), likeCount: 0, likedByUser: false)
        let post3 = PostModel(postID: "", userID: "", username: "Emily", caption: "This is a really really long caption hahahahahaha", dateCreated: Date(), likeCount: 0, likedByUser: false)
        let post4 = PostModel(postID: "", userID: "", username: "Christopher",caption: nil, dateCreated: Date(), likeCount: 0, likedByUser: false)
        
        self.dataArray.append(post1)
        self.dataArray.append(post2)
        self.dataArray.append(post3)
        self.dataArray.append(post4)
    }    
}


피드뷰에 posts 어레이 넣어주고 ForEach로 그려주면 됩니다


한가지 생각 못했던 건 LazyVStack으로 감싸는 거!
이건 한번에 진짜 많은 포스트 안뿌릴라고 감싸줌

Creating the Comments screen

struct CommentsView: View {
    @State var submissionText: String = ""
    
    var body: some View {
        VStack {
            ScrollView {
                Text("PlaceHolder")
                Text("PlaceHolder")
                Text("PlaceHolder")
                Text("PlaceHolder")
                Text("PlaceHolder")
            }
            
            HStack {
                Image("dog1")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 40, height: 40)
                    .cornerRadius(20)
                TextField("Add a comment here...", text: $submissionText)
                
                Button {
                    
                } label: {
                    Image(systemName: "paperplane.fill")
                        .font(.title2)
                }
                .tint(Color.MyTheme.purpleColor)
            }
            .padding(.all, 6)
        }
        .navigationTitle("Comments")
        .navigationBarTitleDisplayMode(.inline)
    }
}

Comments 뷰를 구성!

Creating a MessageView


메시지뷰 만들어주고

그리고 메시지뷰에 들어갈 내용들이 될 CommentModel도 작성해줘야함

코멘트 모델 만들어주고


메시지뷰에서 @State로 선언해줬습니다


이제 코멘트뷰에서 commentArray를 선언해두고 가져와주면 되겠죠

우선은 onAppear일 때 getComments 메소드로 가져와지게 임시로 해둠

글구 피드뷰에서 댓글버튼 누르면 들어가져야겠죠


NavigationLink 써서!!

Creating the Browse screen

BrowseView 만들자
요 안에 Carousel 뷰 들어갈거!

TabView 페이징 되는 형태로 만들어주고
여기에 도그 이미지 7개 다 넣어줄 건데 ForEach써서 그려주자


ForEach 범위에 1부터 시작한건 dog이미지가 1부터 네이밍되어 있어서 그럼


timer 만들고 페이징되게 해줬으


확장성 위해서 사진의 끝 이미지의 Index를 maxCount라는 상수로 빼주고
(이 때 노랑에러 해결할라면 id: .self 붙여주면 됨)

이제 이거를 브라우즈 뷰에 넣어줘야하는데
지금 타이머로직을 .onAppear에 넣어줬잖음
그럼 .onAppear일 때 타이머가 여러번 실행될겨
이거 수정해주자


타이머added로직 추가하고
이게 온 어피어일때 딱 한번 트루로 바뀌게해주면
한번만 로직이 실행되겠죠

ImageGridView

그리드 뷰를 만듭시다


LazyVGrid를 사용하는데
여기 들어갈 내용들은 PostView를 재사용하면 될 거 같다
그러려면 PostView구조를 쪼꼼 바꿔줘야함


showHeaderAndFooter라는 불 값을 만들고 if 문으로 헤더랑 푸터 선택적으로 보여지게 해줌

ImageGridView에도 PostArrayObject를 @ObservedObject로 받게 해주고 여기있는 dataArray를 가지고 뿌려지게 해줌


BrowseView로 돌아와서 ImageGridView를 작성해주는데
여기 들어갈 posts 데이터들 시초가 되는 PostArrayObject를 여기서 선언해줬다

그리고 ImageGridView 아이템들 터치하면 네비게이션 링크 되게도 해주고!
근데 여기서 터치해서 들어가면 FeedView처럼 여러개가 나오는 게 아니라 하나의 PostView (클릭한 내용이 담긴) 가 보여져야함


PostArrayObject에 또 다른 init메소드를 작성해줌!!

🤔(이러면 다른 객체로 init을 하게 되는 걸까?)
오호오오 그렇네요!! 새로운 객체가 만들어집니다


그럼 정리해보자면 ImageGridView에선 PostArrayObject를 받는데 이 때 사용하는 친구는 init()으로 생성된 애고, 얘가 가지고 있는 dataArray를 이용해서 ForEach로 그려주는데 이 때 post를 넘겨서 destination이 되는 애는 FeedView에다가 PostArrayObject(post: )로 생성한 하나짜리 dataArray를 가지고 있는 객체!!
호오오...
이렇게하면 어떤 장점이 있을까?
네트워크로 받는 dataArray랑 post 단일 객체로 쓰는 거랑 구분을 지어서 하나의 class로 만들 수 있어서 그런가?

지금처럼 Browse 뷰에서 ForEach 써서 NavigationLink 만들면 한번에 데이터들 많이 로드 되서 앱 속도가 느려질 거 같음
Crypto App에서 그랬던 것처럼 옵셔널로 받아서 한번 감싸주는 로딩 뷰를 구성해줘야하는 건 아닐지..

하나 더 수정할 건
BrowseView에서 FeedView로 네비게이션 타고 들어가졌을 때 navigationTitle을 수정해줘야할 거 같습니다


요렇게 title구멍 뚫어줘서 말이죠!
그럼 피드탭에서랑 Browse뷰에서 네비게이션 타고 들어갔을 때
제목을 다르게 설정할 수 있습니다~!~!


UploadView


ImagePicker


ImagePicker를 사용하기 위해선
UIKit의 UiViewController를 변환해서 사용해야함

struct ImagePicker: UIViewControllerRepresentable {
    @Environment(\.dismiss) var dismiss
    @Binding var imageSelected: UIImage
    @Binding var sourceType: UIImagePickerController.SourceType
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = sourceType
        picker.allowsEditing = true
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: UIViewControllerRepresentableContext<ImagePicker>) { }
    
    func makeCoordinator() -> ImagePickerCoordinator {
        return ImagePickerCoordinator(parent: self)
    }
    
    class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        
        let parent: ImagePicker
        
        init(parent: ImagePicker) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            
            if let image = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage {
                // select the image for our app
                parent.imageSelected = image
                
                // dismiss the screen
                parent.dismiss()
            }
        }
    }    
}

업로드 뷰로 돌아와서 만들어준 ImagePicker뷰를 넣어줍니다


🤔 photoLibrary의 경우 PHPicker로 추후에 대체해서 사용해야 한다! Deprecated 된다고 함

그리고 이제 액세스에 대한 권한을 물어볼 수 있게 해줘야 하는데


info 리스트에 두가지 (photo library, camera usage) 추가해줍시다

PostImageView


.textInputAuto 메소드로 캐피털 설정 가능

import SwiftUI

struct PostImageView: View {
    @Environment(\.dismiss) var dismiss
    @State var captionText: String = ""
    @Binding var imageSelected: UIImage
    
    var body: some View {
        VStack(spacing: 0) {
            
            HStack {
                Button {
                    dismiss()
                } label: {
                    Image(systemName: "xmark")
                        .font(.title)
                        .padding()
                }
                .tint(.primary)
                Spacer()
            }
            
            ScrollView(.vertical, showsIndicators: false) {
                Image(uiImage: imageSelected)
                    .resizable()
                    .scaledToFill()
                    .frame(width: 200, height: 200)
                    .cornerRadius(12)
                    .clipped()
                TextField("Add your caption here...", text: $captionText)
                    .padding()
                    .frame(height: 60)
                    .frame(maxWidth: .infinity)
                    .background(Color.MyTheme.beigeColor.cornerRadius(12))
                    .font(.headline)
                    .padding(.horizontal)
                    .textInputAutocapitalization(.sentences)
                
                Button {
                    postPicture()
                } label: {
                    Text("Post Picture!".uppercased())
                        .font(.title3)
                        .fontWeight(.bold)
                        .frame(height: 60)
                        .frame(maxWidth: .infinity)
                        .background(Color.MyTheme.purpleColor.cornerRadius(12))
                        .padding(.horizontal)
                }
                .tint(Color.MyTheme.yellowColor)

            }
        }
    }
    
    //MARK: - Functions
    
    func postPicture() {
        print("Post Picture To Database Here")
    }
    
}

struct PostImageView_Previews: PreviewProvider {
    @State static var image = UIImage(named: "dog1")!
    
    static var previews: some View {
        PostImageView(imageSelected: $image)
    }
}

UI구성해주고 button눌렸을 때
postPicture메소드가 호출되게 해줌
지금은 없지만 데이터베이스에 저장되게 해줄 예정

업로드 뷰로 돌아와서


.sheet이랑 겹치지 않게 .fullScreenCover를 달아줬다!
그리고 imageSelected에 이미지 바인딩해줌

이 풀스크린으로 뜨는건 .sheet이 디스미스 될 때 일어날 예정이니까


아래의 onDismiss까지 있는 메소드로 바꿔주자


ProfileView


그리고 헤더를 따로 빼줌!

ImageGridView를 재사용합시다!



Settings View


재사용할 수 있는 것들은 SubView로 만들어줬다!!

재사용 가능한 SubView에서 let이 아니라 var 로 파라미터를 뚫어줬는데 왜그럴까?



SettingsRowView 네비게이션 링크 넣으려구 했는데
색깔 바껴서 .foregroundColor 추가해줌!
.tint로 바꾸는 것 말고도 .foreground로도 가능하다는 걸 알게됐다!


텍스트 필드 가끔 캔버스에서 텍스트입력하려구 하면 스캔텍스트 뜰 때 있는데
시뮬레이터로 한번 실행해주면 돌아온다!


이미지 수정해주는 뷰


어플리케이션 정책 같은 것들은 사이트로 넘어가지면 될 거 같다

struct내에 openCustomURL이라는 메소드를 작성해줌
Link를 쓰는 방법 말고 요렇게도 할 수 있구나!



세팅뷰 프로필뷰에서 띄워주려고 하는데
sheet으로 띄워줄라고 했음!

근데 처음 토글될땐 괜찮은데 두번째부터 캔버스 프리뷰 크래쉬 난다

🤔 왜지?

시뮬레이터에선 잘나옴!


SignUpView


콘텐트뷰에서 signin을 했냐 안했냐에 따라서 profileView를 보여줘야함

.tabItem같은 경우에 안에 있는 애들이 어떤 애든 상관없이 나와야해서 밖에 빼줬다


Onboarding View

애플 같은 경우 로그인 버튼의 디자인을 특정해놔서 이거에 맞춰서 만들어야함
애플 로그인 버튼을 넣어줄건데
UIKit을 조금 사용해야한다


요호!


구글 로그인 버튼도 만들어주고~


이렇게 하고 지금까지 만든 뷰들 .sheet이나 .fullScreen으로 열리게 연결해줌


LikeAnimation View



ImageGridView에서 LikeAnimationView때문에 PostView 레이아웃이 이상해졌다
닉은 Bool값을 하나더 만들어줬는데 나는 그냥 overlay로 올려줬으

Adding Ability to share and report

PostView에 ActionSheet 추가해주자

actionSheet은 deprecated 될 예정 다르게 바꾼다면?
confirmationDialogue가 있긴한데 지금처럼 여러개의 버튼이 나오지는 않네요


오오 요렇게 구현해주면 됨!!


다시 actionsheet으로 돌아와서


func getActionSheet() -> ActionSheet {
        
        switch self.actionSheetType {
        case .general:
            return ActionSheet(title: Text("What would you like to do?"), message: nil, buttons: [
                .destructive(Text("Report"), action: {
                    print("Report Post")
                    self.actionSheetType = .reporting
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                        self.showActionSheet.toggle()
                    }
                }),
                .default(Text("Learn more..."), action: {
                    print("Laern more Pressed")
                }),
                .cancel()
            ])
            
        case .reporting:
            return ActionSheet(title: Text("Why are you reporting this post?"), message: nil, buttons: [
                .destructive(Text("This is inappropriate")) {
                    reportPost(reason: "This is inappropriate")
                },
                .destructive(Text("This is spam")) {
                    reportPost(reason: "This is spam")
                },
                .destructive(Text("It made me uncomfortable")) {
                    reportPost(reason: "It made me uncomfortable")
                },
                .cancel({
                    self.actionSheetType = .general
                })
            ])
        }                
    }
    
    func reportPost(reason: String) {
        print("Report Post Now")
    }

report를 누르면 actionSheetType이 바뀌고 다시 actionSheet이 뜨게 해줬음

cancel할 때 다시 general로 바뀌게 해주고!


Share기능 만들기!!

Share를 고도화해서 만들 수 있는데
지금은 그냥 간단하게 만들어봅시다

🤔ViewController를 선언해준 UIApplication.shared.windows가
deprecated 예정임.. 다른 표현 찾아보기!!!


Support Dark Mode


요렇게 ColorScheme추가해줄수도 있습니다~!

profile
우주형

1개의 댓글

comment-user-thumbnail
2024년 1월 16일

안녕하세요! 글 잘 봤습니다 ㅎㅎㅎ
혹시 피드에서 오른쪽으로 스와이프 했을때 카메라가 나오게 하는 인터렉션은 어떻게 구현할 수 있을까요?

답글 달기