Skip to content

Instantly share code, notes, and snippets.

@zrzka
Last active February 12, 2020 14:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zrzka/d894e41cfa61be1aa4a4f3243b66a087 to your computer and use it in GitHub Desktop.
Save zrzka/d894e41cfa61be1aa4a4f3243b66a087 to your computer and use it in GitHub Desktop.
SwiftUI Keyboard Adapter
import Combine
import SwiftUI
import UIKit
private struct KeyboardInfo {
let height: CGFloat
let eventuallyVisible: Bool
private let animationDuration: Double
var animation: Animation {
.easeOut(duration: animationDuration)
}
var edgesIgnoringSafeArea: Edge.Set {
eventuallyVisible ? [.bottom] : []
}
init() {
self.eventuallyVisible = false
self.height = 0
self.animationDuration = 0
}
init?(notification: Notification) {
guard let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber else {
return nil
}
switch notification.name {
case UIResponder.keyboardWillShowNotification, UIResponder.keyboardWillChangeFrameNotification:
self.eventuallyVisible = true
self.height = frame.size.height
default:
self.eventuallyVisible = false
self.height = 0
}
self.animationDuration = duration.doubleValue
}
}
private final class Keyboard: ObservableObject {
@Published var info = KeyboardInfo()
private var subscription: AnyCancellable?
func cancelKeyboardEventsSubscription() {
subscription?.cancel()
subscription = nil
}
func subscribeToKeyboardEvents() {
guard subscription == nil else { return }
subscription = Publishers.Sequence(sequence: [
UIResponder.keyboardWillShowNotification,
UIResponder.keyboardWillHideNotification,
UIResponder.keyboardWillChangeFrameNotification,
])
.map { NotificationCenter.default.publisher(for: $0) }
.flatMap { Publishers.MergeMany($0) }
.map { KeyboardInfo(notification: $0) }
.compactMap { $0 }
.receive(on: RunLoop.main)
.assign(to: \.info, on: self)
}
}
struct KeyboardAdapter: ViewModifier {
@ObservedObject private var keyboard = Keyboard()
func body(content: Content) -> some View {
content
.padding(.bottom, keyboard.info.height)
.animation(keyboard.info.animation)
.edgesIgnoringSafeArea(keyboard.info.edgesIgnoringSafeArea)
.onAppear(perform: keyboard.subscribeToKeyboardEvents)
.onDisappear(perform: keyboard.cancelKeyboardEventsSubscription)
}
}
extension View {
func adaptToKeyboard() -> some View {
self.modifier(KeyboardAdapter())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment