mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-26 19:34:25 +02:00
@@ -48,44 +48,31 @@ struct SecondaryActionButtonStyle: ButtonStyle {
|
||||
struct SecondaryActionButtonStyle_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
VStack {
|
||||
Button("Enabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
|
||||
Button("Disabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
.disabled(true)
|
||||
|
||||
Button { } label: {
|
||||
Text("Clear BG")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .clear))
|
||||
|
||||
Button("Red BG") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .red))
|
||||
}
|
||||
.padding()
|
||||
buttonGroup
|
||||
}.theme(.light).preferredColorScheme(.light)
|
||||
Group {
|
||||
VStack {
|
||||
Button("Enabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
|
||||
Button("Disabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
.disabled(true)
|
||||
|
||||
Button { } label: {
|
||||
Text("Clear BG")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .clear))
|
||||
|
||||
Button("Red BG") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .red))
|
||||
}
|
||||
.padding()
|
||||
buttonGroup
|
||||
}.theme(.dark).preferredColorScheme(.dark)
|
||||
}
|
||||
|
||||
static var buttonGroup: some View {
|
||||
VStack {
|
||||
Button("Enabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
|
||||
Button("Disabled") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle())
|
||||
.disabled(true)
|
||||
|
||||
Button { } label: {
|
||||
Text("Clear BG")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .clear))
|
||||
|
||||
Button("Red BG") { }
|
||||
.buttonStyle(SecondaryActionButtonStyle(customColor: .red))
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,74 +26,3 @@ extension View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - modal presentation mode
|
||||
|
||||
fileprivate class PresentationControllerDelegate: NSObject, UIAdaptivePresentationControllerDelegate {
|
||||
private let callback: (() -> Void)?
|
||||
|
||||
init(callback: @escaping () -> Void) {
|
||||
self.callback = callback
|
||||
}
|
||||
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
callback?()
|
||||
}
|
||||
}
|
||||
//fileprivate var currentPresentationControllerDelegate: PresentationControllerDelegate? = nil
|
||||
fileprivate var presentationControllerDelegates: [String: PresentationControllerDelegate] = [:]
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
//fileprivate var currentOverCurrentContextUIHost: UIHostingController<AnyView>? = nil
|
||||
@available(iOS 13.0, *)
|
||||
fileprivate var uiHosts: [String: UIHostingController<AnyView>] = [:]
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
extension View {
|
||||
|
||||
func modal<Content>(
|
||||
withStyle modalPresentationStyle: UIModalPresentationStyle,
|
||||
animated: Bool = true,
|
||||
modalTransitionStyle: UIModalTransitionStyle? = nil,
|
||||
id: String,
|
||||
isPresented: Binding<Bool>,
|
||||
content: () -> Content
|
||||
) -> some View where Content: View {
|
||||
if isPresented.wrappedValue && uiHosts[id] == nil {
|
||||
let uiHost = UIHostingController(rootView: AnyView(content()))
|
||||
uiHosts[id] = uiHost
|
||||
|
||||
uiHost.modalPresentationStyle = modalPresentationStyle
|
||||
if let modalTransitionStyle = modalTransitionStyle {
|
||||
uiHost.modalTransitionStyle = modalTransitionStyle
|
||||
}
|
||||
uiHost.view.backgroundColor = UIColor.clear
|
||||
presentationControllerDelegates[id] = PresentationControllerDelegate(callback: {
|
||||
isPresented.wrappedValue = false
|
||||
})
|
||||
uiHost.presentationController?.delegate = presentationControllerDelegates[id]
|
||||
|
||||
if let rootVC = UIApplication.shared.windows.first?.rootViewController {
|
||||
frontmostPresentedViewController(from: rootVC)?.present(uiHost, animated: animated, completion: nil)
|
||||
}
|
||||
} else {
|
||||
if let uiHost = uiHosts[id] {
|
||||
uiHost.dismiss(animated: animated, completion: {})
|
||||
uiHosts.removeValue(forKey: id)
|
||||
presentationControllerDelegates.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
fileprivate func frontmostPresentedViewController(from viewController: UIViewController) -> UIViewController? {
|
||||
var frontMostController = viewController
|
||||
while let presentedViewController = frontMostController.presentedViewController {
|
||||
frontMostController = presentedViewController
|
||||
}
|
||||
|
||||
return frontMostController
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct WaitModalContentView: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var alignment: Alignment = .center
|
||||
@State var message: String? = nil
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.black.opacity(0.6)
|
||||
VStack(spacing: 12) {
|
||||
ProgressView()
|
||||
.scaleEffect(1.3, anchor: .center)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: theme.colors.secondaryContent))
|
||||
if let message = message {
|
||||
Text(message)
|
||||
.font(theme.fonts.callout)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
}
|
||||
}
|
||||
.padding(12)
|
||||
.background(RoundedRectangle(cornerRadius: 8, style: .continuous)
|
||||
.fill(theme.colors.navigation.opacity(0.9)))
|
||||
}
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct WaitModalView: ViewModifier {
|
||||
// MARK: - Properties
|
||||
|
||||
var alignment: Alignment = .center
|
||||
@State var message: String? = nil
|
||||
@Binding var isLoading: Bool
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
public func body(content: Content) -> some View
|
||||
{
|
||||
content
|
||||
.modal(withStyle: .overFullScreen,
|
||||
modalTransitionStyle: .crossDissolve,
|
||||
id: "WaitModalView",
|
||||
isPresented: $isLoading) {
|
||||
WaitModalContentView(alignment: alignment, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
private var contentView: some View {
|
||||
ZStack {
|
||||
theme.colors.primaryContent.opacity(0.75)
|
||||
VStack(spacing: 12) {
|
||||
ProgressView()
|
||||
.scaleEffect(1.3, anchor: .center)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: theme.colors.secondaryContent))
|
||||
if let message = message {
|
||||
Text(message)
|
||||
.font(theme.fonts.callout)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
}
|
||||
}
|
||||
.padding(12)
|
||||
.background(RoundedRectangle(cornerRadius: 8, style: .continuous)
|
||||
.fill(theme.colors.navigation.opacity(0.9)))
|
||||
}
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct WaitModalView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
VStack {
|
||||
ThemableNavigationBar(title: nil, showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: nil, showBackButton: false, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {})
|
||||
}
|
||||
.modifier(WaitModalView(isLoading: .constant(true)))
|
||||
VStack {
|
||||
ThemableNavigationBar(title: nil, showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: nil, showBackButton: false, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {})
|
||||
}
|
||||
.modifier(WaitModalView(alignment:.topLeading, isLoading: .constant(true)))
|
||||
VStack {
|
||||
ThemableNavigationBar(title: nil, showBackButton: true, backAction: {}, closeAction: {}).theme(.dark)
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: true, backAction: {}, closeAction: {}).theme(.dark)
|
||||
ThemableNavigationBar(title: nil, showBackButton: false, backAction: {}, closeAction: {}).theme(.dark)
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {}).theme(.dark)
|
||||
}
|
||||
|
||||
.modifier(WaitModalView(isLoading: .constant(true))).theme(.dark)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,32 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
/// A modifier for showing the wait overlay view over a view.
|
||||
struct WaitOverlayModifier: ViewModifier {
|
||||
|
||||
var allowUserInteraction: Bool
|
||||
var show: Bool
|
||||
var message: String?
|
||||
|
||||
@ViewBuilder
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.modifier(WaitOverlay(
|
||||
allowUserInteraction: allowUserInteraction,
|
||||
message: message,
|
||||
isLoading: show))
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
extension View {
|
||||
@available(iOS 14.0, *)
|
||||
func waitOverlay(show: Bool, message: String? = nil, allowUserInteraction: Bool = true) -> some View {
|
||||
self.modifier(WaitOverlayModifier(allowUserInteraction: allowUserInteraction, show: show, message: message))
|
||||
}
|
||||
}
|
||||
|
||||
/// `WaitOverlay` allows to easily add an overlay that covers the entire with an `ActivityIndicator` at the center
|
||||
@available(iOS 14.0, *)
|
||||
struct WaitOverlay: ViewModifier {
|
||||
@@ -23,9 +49,9 @@ struct WaitOverlay: ViewModifier {
|
||||
|
||||
var alignment: Alignment = .center
|
||||
var allowUserInteraction: Bool = true
|
||||
@Binding var message: String?
|
||||
@Binding var isLoading: Bool
|
||||
|
||||
var message: String?
|
||||
var isLoading: Bool
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
@@ -34,14 +60,14 @@ struct WaitOverlay: ViewModifier {
|
||||
|
||||
init(alignment: Alignment = .center,
|
||||
allowUserInteraction: Bool = true,
|
||||
message: Binding<String?> = .constant(nil),
|
||||
isLoading: Binding<Bool>) {
|
||||
_message = message
|
||||
_isLoading = isLoading
|
||||
message: String? = nil,
|
||||
isLoading: Bool) {
|
||||
self.message = message
|
||||
self.isLoading = isLoading
|
||||
self.alignment = alignment
|
||||
self.allowUserInteraction = allowUserInteraction
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
public func body(content: Content) -> some View
|
||||
@@ -78,7 +104,7 @@ struct WaitOverlay: ViewModifier {
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct WaitView_Previews: PreviewProvider {
|
||||
struct WaitOverlay_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
VStack {
|
||||
@@ -87,14 +113,14 @@ struct WaitView_Previews: PreviewProvider {
|
||||
ThemableNavigationBar(title: nil, showBackButton: false, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {})
|
||||
}
|
||||
.modifier(WaitOverlay(isLoading: .constant(true)))
|
||||
.modifier(WaitOverlay(isLoading: true))
|
||||
VStack {
|
||||
ThemableNavigationBar(title: nil, showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: true, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: nil, showBackButton: false, backAction: {}, closeAction: {})
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {})
|
||||
}
|
||||
.modifier(WaitOverlay(alignment:.topLeading, isLoading: .constant(true)))
|
||||
.modifier(WaitOverlay(alignment:.topLeading, isLoading: true))
|
||||
VStack {
|
||||
ThemableNavigationBar(title: nil, showBackButton: true, backAction: {}, closeAction: {}).theme(.dark)
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: true, backAction: {}, closeAction: {}).theme(.dark)
|
||||
@@ -102,7 +128,7 @@ struct WaitView_Previews: PreviewProvider {
|
||||
ThemableNavigationBar(title: "Some Title", showBackButton: false, backAction: {}, closeAction: {}).theme(.dark)
|
||||
}
|
||||
|
||||
.modifier(WaitOverlay(isLoading: .constant(true))).theme(.dark)
|
||||
.modifier(WaitOverlay(isLoading: true)).theme(.dark)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Reference in New Issue
Block a user