import Combine import SwiftUI struct RoundedBackground: ViewModifier { private let color: Color init(color: Color = Color("CapsuleColor")) { self.color = color } func body(content: Content) -> some View { content .padding() .background( RoundedRectangle(cornerRadius: 8, style: .continuous) .fill() .foregroundColor(color) ) } } struct CapsulaBackground: ViewModifier { private let color: Color init(color: Color = Color("CapsuleColor")) { self.color = color } func body(content: Content) -> some View { content .padding() .background( Capsule() .fill() .foregroundColor(color) ) } } struct Link: ViewModifier where T: View { private let destination: T init(destination: T) { self.destination = destination } func body(content: Content) -> some View { ZStack { NavigationLink(destination: destination) { EmptyView() }.hidden() content } } } struct AdaptsToSoftwareKeyboard: ViewModifier { @State var currentHeight: CGFloat = 0 func body(content: Content) -> some View { content .padding(.bottom, currentHeight).animation(.easeOut(duration: 0.25)) .edgesIgnoringSafeArea(currentHeight == 0 ? Edge.Set() : .bottom) .onAppear(perform: subscribeToKeyboardChanges) } private let keyboardHeightOnOpening = Foundation.NotificationCenter.default .publisher(for: UIResponder.keyboardWillShowNotification) .map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect } .map(\.height) private let keyboardHeightOnHiding = Foundation.NotificationCenter.default .publisher(for: UIResponder.keyboardWillHideNotification) .map { _ in CGFloat(0) } private func subscribeToKeyboardChanges() { _ = Publishers.Merge(keyboardHeightOnOpening, keyboardHeightOnHiding) .subscribe(on: DispatchQueue.main) .sink { height in if self.currentHeight == 0 || height == 0 { self.currentHeight = height } } } } struct ClearButton: ViewModifier { @Binding var text: String func body(content: Content) -> some View { HStack { content if !text.isEmpty { Button { self.text = "" } label: { Image(systemName: "delete.left") .foregroundColor(.gray) } } } } } struct ChevronCell: ViewModifier { func body(content: Content) -> some View { HStack { content Spacer() Image(systemName: "chevron.forward").foregroundColor(.secondary) }.contentShape(Rectangle()) } } extension View { func roundedBackground() -> some View { modifier(RoundedBackground()) } func buttonBackground() -> some View { modifier(RoundedBackground(color: .accentColor)) } func navigationLink(to screen: Screen, from view: V) -> some View { modifier(Link(destination: view.viewModel.view(for: screen))) } func adaptsToSoftwareKeyboard() -> some View { modifier(AdaptsToSoftwareKeyboard()) } func modal(for screen: Screen?, from view: V) -> some View { onTapGesture { view.viewModel.showModal(for: screen) } } func asAny() -> AnyView { .init(self) } func chevronCell() -> some View { modifier(ChevronCell()) } }