SwiftUI: глобальное наложение, которое может быть запущено из любого представления - PullRequest
6 голосов
/ 11 июня 2019

Я довольно новичок в фреймворке SwiftUI, и я еще не обнял все это, поэтому, пожалуйста, потерпите меня.

Есть ли способ вызвать "вид наложения" изнутри«другой взгляд», когда меняется его привязка?См. Иллюстрацию ниже:

enter image description here

Я полагаю, что этот «вид наложения» обернет все мои виды.Я пока не уверен, как это сделать - возможно, используя ZIndex.Я также думаю, что мне понадобится какой-то обратный вызов, когда привязка изменится, но я также не уверен, как это сделать.

Это то, что я получил до сих пор:

ContentView

struct ContentView : View {
    @State private var liked: Bool = false

    var body: some View {
        VStack {
            LikeButton(liked: $liked)
        }
    }
}

LikeButton

struct LikeButton : View {
    @Binding var liked: Bool

    var body: some View {
        Button(action: { self.toggleLiked() }) {
            Image(systemName: liked ? "heart" : "heart.fill")
        }
    }

    private func toggleLiked() {
        self.liked = !self.liked
        // NEED SOME SORT OF TOAST CALLBACK HERE
    }
}

Я чувствую, что мне нужен какой-то обратный вызов внутри моего LikeButton, но яЯ не уверен, как все это работает в Swift.

Любая помощь с этим будет оценена.Заранее спасибо!

Ответы [ 2 ]

11 голосов
/ 11 июня 2019

Довольно просто - и интересно - построить «тост» в SwiftUI!

Давайте сделаем это!

struct Toast<Presenting>: View where Presenting: View {

    /// The binding that decides the appropriate drawing in the body.
    @Binding var isShowing: Bool
    /// The view that will be "presenting" this toast
    let presenting: () -> Presenting
    /// The text to show
    let text: Text

    var body: some View {

        GeometryReader { geometry in

            ZStack(alignment: .center) {

                self.presenting()
                    .blur(radius: self.isShowing ? 1 : 0)

                VStack {
                    self.text
                }
                .frame(width: geometry.size.width / 2,
                       height: geometry.size.height / 5)
                .background(Color.secondary.colorInvert())
                .foregroundColor(Color.primary)
                .cornerRadius(20)
                .transition(.slide)
                .opacity(self.isShowing ? 1 : 0)

            }

        }

    }

}

Объяснение тела:

  • GeometryReader дает нам предпочтительный размер суперпредставления, позволяя таким образом идеально подобрать размеры для наших Toast.
  • ZStack стеков друг над другом.
  • Логикатривиально: если тост не предполагается увидеть (isShowing == false), то мы визуализируем представление presenting.Если необходимо представить тост (isShowing == true), то мы визуализируем представление presenting с небольшим размытием - потому что мы можем - и мы создаем наш тост следующим образом.
  • Тост - это простоVStack с Text, с пользовательским размером рамки, некоторыми дизайнерскими колокольчиками (цветами и радиусом угла) и переходом по умолчанию slide.

Я добавил этот метод на View чтобы упростить создание Toast:

extension View {

    func toast(isShowing: Binding<Bool>, text: Text) -> some View {
        Toast(isShowing: isShowing,
              presenting: { self },
              text: text)
    }

}

И небольшая демонстрация того, как его использовать:

struct ContentView: View {

    @State var showToast: Bool = false

    var body: some View {
        NavigationView {
            List(0..<100) { item in
                Text("\(item)")
            }
            .navigationBarTitle(Text("A List"), displayMode: .large)
            .navigationBarItems(trailing: Button(action: {
                withAnimation {
                    self.showToast.toggle()
                }
            }){
                Text("Toggle toast")
            })
        }
        .toast(isShowing: $showToast, text: Text("Hello toast!"))
    }

}

Я использовал NavigationView, чтобы убедиться, что вид заполняетсявесь экран, поэтому Toast имеет размеры и правильное расположение.

Блок withAnimation обеспечивает применение перехода Toast.


Как это выглядит:

enter image description here

Расширение Toast с помощью DSL SwiftUI легко.

Textсвойство может легко стать закрытием @ViewBuilder для размещения наиболее экстравагантных макетов.


Чтобы добавить его в представление содержимого :

struct ContentView : View {
    @State private var liked: Bool = false

    var body: some View {
        VStack {
            LikeButton(liked: $liked)
        }
        // make it bigger by using "frame" or wrapping it in "NavigationView"
        .toast(isShowing: $liked, text: Text("Hello toast!"))
    }
}

Как скрыть тост после 2 секунд (по запросу) :

Добавить этот код после .transition(.slide) в тосте VStack.

.onAppear {
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
      withAnimation {
        self.isShowing = false
      }
    }
}
0 голосов
/ 11 июня 2019

Используйте .presentation(), чтобы отобразить предупреждение при нажатии кнопки.

В LikeButton:

@Binding var liked: Bool

var body: some View {
    Button(action: {self.liked = !self.liked}, label: {
        Image(systemName: liked ? "heart.fill" : "heart")
    }).presentation($liked) { () -> Alert in
        Alert.init(title: Text("Thanks for liking!"))
    }
}

Вы также можете использовать .presentation() для представления других модальных представлений, таких как Popover или ActionSheet. См. здесь и раздел «См. Также» на этой странице в документации Apple SwiftUI для получения информации о различных параметрах .presentation().

Редактировать : пример того, что вы хотите с пользовательским представлением, используя Popover:

@State var liked = false
let popover = Popover(content: Text("Thanks for liking!").frame(width: 200, height: 100).background(Color.white), dismissHandler: {})
var body: some View {
    Button(action: {self.liked = !self.liked}, label: {
        Image(systemName: liked ? "heart.fill" : "heart")
    }).presentation(liked ? popover : nil)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...