김니은 
KimN's Blog
김니은 
  • 분류 전체보기
    • Algorithm
      • Programmers
    • Develop
      • Swift
      • Flask
      • RubyonRails
      • AWS
      • Ardunio
      • Vue
      • Node.js
      • Infra
      • CS
    • IT Story
      • Hackintosh
      • GitHub
      • IT Review

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

태그

  • SWIFTUI
  • Ruby
  • Ruby on Rails
  • TOAST
  • 카카오 챗봇
  • 항상 맨 위
  • Code Snippet
  • SWIFT

최근 댓글

최근 글

티스토리

반응형
hELLO
김니은 
Develop/Swift

[Swift] 토스트(Toast) Message WindowScene 연구

[Swift] 토스트(Toast) Message WindowScene 연구
Develop/Swift

[Swift] 토스트(Toast) Message WindowScene 연구

2022. 5. 7. 19:58
반응형

 

 

안드로이드에서는 기본적으로 있지만 swift에서는 직접 구현해주어야합니다.

 

toast 관련해서 라이브러리도 존재하고 swiftUI ViewModifier로 구현한 예제도 많습니다.

 

하지만 안드로이드랑 다르게 현재 자신의 뷰에서만 표시되고 이전 뷰로 가거나 다음 뷰로 이동 시, 토스트가 사라지거나 가려지는 문제가 있었습니다.

 

그래서 연구하는 도중 WindowScene 맨 위로 올리면 어떻게 될까 구현해본 결과, 항상 맨위에 존재하는 것을 확인할 수 있었습니다.

 

 

 

위의 이미지와 같이 뷰가 이전으로 가거나 다시 생기더라도 항상 Toast가 맨 위에 존재하는 것을 확인할 수 있습니다.

 

 

 

1. Get WindowScenes

 

func getWindowScenes() -> [UIWindow] {
    return UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
}
 

해당 코드는 windowScene를 Get하는 코드입니다. UIApplication.shared.windows이 deprecated가 났기 때문에 저는 따로 코드를 분리하여 얻어오도록 하였습니다.

 

 

 

2. Show & Dismiss

 

// 토스트 화면 View
var toastView: UIView?

...

// 토스트 화면 Show
func showToastView(message: String) {
    let window = getWindowScenes().last

    toastView = UIHostingController(rootView: ToastView(message: message,
    													   edge: window?.safeAreaInsets))).view

    guard let toastView = toastView else {
        return
    }
    
    window?.addSubview(toastView)
}

// 토스트 화면 Dismiss
func dismissToastView() {
    toastView?.removeFromSuperview()
    toastView = nil
}
 
windowScene의 last에 toastView를 추가해 줍니다. 이후 모두 사용해주었다면 removeFromSuperview로 제거해줍니다.
edge를 매개변수에 추가한 이유는 ToastView를 SwiftUI로 제작하는데 Geometry safearea가 잘 안나오고 있었습니다.
확실하지 않지만, 아마 새 window에 ToastView만 따로 추가해서 그런지 부모 View가 없어서 감지를 못하는 것 같았습니다.
그래서 window의 edge를 같이 변수에 추가하였습니다.
 
 

 

3. ToastView Example

 

import SwiftUI

struct ToastView: View {
    @State var isShowing: Bool = true
    @State var width: CGFloat = 0
    @State var height: CGFloat = 0

    var message: String = ""
    var safeArea: CGFloat = 0
    
    init(message: String, edge: UIEdgeInsets?) {
        self.message = message
        self.safeArea = max(max(edge.top, edge.bottom), max(edge.left, edge.right))
        self._width = State(initialValue: getX(orientation: [ 현 디바이스 회전 상태 ]))
        self._height = State(initialValue: UIScreen.main.bounds.size.height * 0.8)
    }
    
    var body: some View {
        GeometryReader { _ in
            if isShowing == true {
                VStack {
                    Color.black.opacity([ 원하는 value ])
                        .frame(width: [ 원하는 크기 ], height: [ 원하는 크기 ])
                        .offset(x: width, y: height)
                        // 원하는 View 속성
                        .viewOverlay(alignment: .center, content: {
                            Text(message)
                            	// 원하는 Text 속성
                                .multilineTextAlignment(.center)
                                .offset(x: width, y: height)
                        })
                }.onRotate { newOrientation in
                    if HardwareInfo.isPadDevice() == false {
                        height = UIScreen.main.bounds.size.height * 0.8
                        width = getX(orientation: newOrientation)
                    } else {
                        width = UIScreen.main.bounds.size.height / 2 - [ 원하는 크기의 절반 ]
                        height = UIScreen.main.bounds.size.width * 0.8
                    }
                }
            }
        }.onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + [ 원하는 시간 ]) {
                self.isShowing = false
                dismissToastView()
            }
        }
    }
    
    fileprivate func getX(orientation: UIDeviceOrientation) -> CGFloat {
        if orientation == .portrait {
            return (UIScreen.main.bounds.size.width / 2) - [ 원하는 크기의 절반 ]
        } else {
            return (UIScreen.main.bounds.size.width / 2) - ([ 원하는 크기의 절반 ] + safeArea)
        }
    }
    
}

이는 제가 예제로 제작한 ToastView 코드입니다. 몇가지 제거한 부분이 있는데 해당 내용은 그냥 이러한 형태로 구현해봤다 라는 느낌만.. 알아주셨으면 합니다 ㅎㅎ

 

 

토스트 뷰가 나올 때 다른 뷰로 이동하거나 현재 뷰를 없애는 경우가 없다면 라이브러리를 사용해도 되지만.. 만약 저처럼 여러 뷰를 옮겨야 하는 상황이라면 고려해보시면 좋을 거 같습니다.

 

반응형
저작자표시 (새창열림)

'Develop > Swift' 카테고리의 다른 글

[iOS] Xcode Code Snippet  (0) 2022.05.08
[Objective-C] message, location으로 매개변수 이름정하면 에러..  (0) 2022.05.07
[SwiftUI] TabView PageStyle 회전 연구 (iPad 기준)  (0) 2022.05.03
[SwiftUI] ViewModifier 사용해보기  (0) 2022.05.03
[SwiftUI] fullScreenCover 고찰  (0) 2022.05.02
    'Develop/Swift' 카테고리의 다른 글
    • [iOS] Xcode Code Snippet
    • [Objective-C] message, location으로 매개변수 이름정하면 에러..
    • [SwiftUI] TabView PageStyle 회전 연구 (iPad 기준)
    • [SwiftUI] ViewModifier 사용해보기
    김니은 
    김니은 

    티스토리툴바

    단축키

    내 블로그

    내 블로그 - 관리자 홈 전환
    Q
    Q
    새 글 쓰기
    W
    W

    블로그 게시글

    글 수정 (권한 있는 경우)
    E
    E
    댓글 영역으로 이동
    C
    C

    모든 영역

    이 페이지의 URL 복사
    S
    S
    맨 위로 이동
    T
    T
    티스토리 홈 이동
    H
    H
    단축키 안내
    Shift + /
    ⇧ + /

    * 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.