Skip to content

Commit

Permalink
fix: show custom keyboard over standard one - #3 #4
Browse files Browse the repository at this point in the history
  • Loading branch information
bsorrentino committed Sep 13, 2022
1 parent fbf5b01 commit ed1f8c1
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 93 deletions.
14 changes: 4 additions & 10 deletions PlantUML/PlantUMLEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ enum Focusable: Hashable {
struct PlantUMLEditorView: View {
@Environment(\.editMode) private var editMode
@Environment(\.openURL) private var openURL

@EnvironmentObject private var diagram: PlantUMLDiagramObject

@ObservedObject var customKeyboard = CustomKeyboardObject()

@Binding var document: PlantUMLDocument

@FocusState private var focusedItem: Focusable?

@State private var isPreviewVisible = false

@State private var showKeyboard = false

var body: some View {
ZStack(alignment: .bottom) {

Expand All @@ -49,13 +50,6 @@ struct PlantUMLEditorView: View {
}
}

if( showKeyboard ) {

PlantUMLKeyboardView(
show: $showKeyboard,
value:Binding.constant("")
)
}
}

}
Expand Down Expand Up @@ -133,7 +127,7 @@ struct PlantUMLEditorView: View {
}
}
PlantUMLTextField( value: item.rawValue,
showKeyboard: $showKeyboard,
showKeyboard: $customKeyboard.showKeyboard,
onChange: updateItem )
.focused($focusedItem, equals: .row(id: item.id))
.onSubmit(of: .text) {
Expand Down
2 changes: 1 addition & 1 deletion PlantUML/PlantUMLTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ extension PlantUMLTextField {

print( "show keyboard")

if let rootViewController = getRootViewController() {
if let rootViewController = getWindows()?.first?.rootViewController {
rootViewController.view.endEditing(true)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import Combine
import SwiftUI


public class CustomKeyboardObject : ObservableObject {

@Published public var showKeyboard = false

private var controller:UIHostingController<PlantUMLKeyboardView>?

private var keyboardRect:CGRect = .zero

private var cancellable:AnyCancellable?

public init() {

NotificationCenter.default.addObserver(

forName: UIResponder.keyboardDidShowNotification, object: nil, queue: .main) { [weak self] notification in

print( "keyboardDidShowNotification: \(notification)" )

if let keyboardFrameEndUser = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue) {

print( "keyboardFrameEndUser pos=\(keyboardFrameEndUser)")

self?.keyboardRect = keyboardFrameEndUser.cgRectValue
}

}

NotificationCenter.default.addObserver(

forName: UIResponder.keyboardDidHideNotification, object: nil, queue: .main) {_ in

print( "keyboardDidHideNotification" )

}

cancellable = _showKeyboard.projectedValue.sink { value in
print( "showKeyboard: \(value)" )

if( value ) {
self.show()
}
else {
self.hide()
}

}

}

//
//[Cannot convert value of type 'Published<Bool>.Publisher' to expected argument type 'Binding<Bool>'](https://stackoverflow.com/a/63282875/521197)
//
private var showKeyboardBinding:Binding<Bool> {
Binding(
get: { [weak self] in
(self?.showKeyboard ?? false)
},
set: { [weak self] in
self?.showKeyboard = $0
}
)
}

private func show() {

guard !self.showKeyboard && self.keyboardRect != .zero, let keyboardWindow = getKeyboardWindow() else {
return
}

print( "keyboardRect: \(keyboardRect)")

let controller = UIHostingController( rootView: PlantUMLKeyboardView( show: showKeyboardBinding) )
self.controller = controller
controller.view.frame = CGRect( origin: keyboardRect.origin, size: CGSize( width: keyboardRect.width, height: 500.0) )
keyboardWindow.addSubview( controller.view )



}

private func hide() {
keyboardRect = .zero
controller?.view.removeFromSuperview()
}
}
89 changes: 7 additions & 82 deletions PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard.swift
Original file line number Diff line number Diff line change
@@ -1,55 +1,6 @@
import SwiftUI
import UIKit

func getFirstWindow() -> UIWindow? {

let scenes = UIApplication.shared.connectedScenes
guard let windowScene = scenes.first as? UIWindowScene else {
return nil
}
guard let window = windowScene.windows.first else {
return nil
}
return window

}

// https://stackoverflow.com/a/1823360/521197
extension UIView {

var firstResponder: UIView? {
guard !isFirstResponder else { return self }

for subview in subviews {
if let firstResponder = subview.firstResponder {
return firstResponder
}
}

return nil
}
}

func getFirstTextFieldResponder() -> UITextField? {

let scenes = UIApplication.shared.connectedScenes
guard let windowScene = scenes.first as? UIWindowScene else {
return nil
}
guard let window = windowScene.windows.first else {
return nil
}

guard let firstResponder = window.firstResponder else {
return nil
}

return firstResponder as? UITextField
}

public func getRootViewController() -> UIViewController? {
getFirstWindow()?.rootViewController
}

struct Symbol : Identifiable, CustomStringConvertible {
var description: String {
Expand Down Expand Up @@ -118,40 +69,13 @@ fileprivate var plantUMLImages:[[UIImage?]] = {
}()


// [StackOverflow](https://stackoverflow.com/a/73628496/521197)
extension UIImage {

func extractTiles(with tileSize: CGSize) -> [UIImage?] {

let hCount = Int(self.size.height / tileSize.height )
let wCount = Int(self.size.width / tileSize.width )

var tiles:[UIImage] = []

for i in 0...hCount-1 {
for p in 0...wCount-1 {
let rect = CGRect(
x: CGFloat(p) * tileSize.width,
y: CGFloat(i) * tileSize.height,
width: tileSize.width,
height: tileSize.height)
let temp:CGImage = self.cgImage!.cropping(to: rect)!
tiles.append(UIImage(cgImage: temp))
}
}
return tiles
}

}

public struct PlantUMLKeyboardView: View {

@Binding var show : Bool
@Binding var value : String

public init( show: Binding<Bool>, value: Binding<String> ) {
public init( show: Binding<Bool> ) {
self._show = show
self._value = value
}

public var body : some View{
Expand Down Expand Up @@ -185,9 +109,8 @@ public struct PlantUMLKeyboardView: View {
.padding(.top)

}
.frame(
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height / 3)
.frame(maxWidth: .infinity)
// .frame( width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 3)
.background(Color.white)
.cornerRadius(25)

Expand All @@ -200,6 +123,8 @@ public struct PlantUMLKeyboardView: View {
}
}

//
//
//
func replaceSymbolAtCursorPosition( _ symbol: Symbol) {
guard let handleToYourTextView = getFirstTextFieldResponder() else {
Expand Down Expand Up @@ -249,7 +174,7 @@ extension PlantUMLKeyboardView {

struct PlantUMLKeyboardView_Previews: PreviewProvider {
static var previews: some View {
PlantUMLKeyboardView( show: Binding.constant(true), value: Binding.constant("TEST"))
PlantUMLKeyboardView( show: Binding.constant(true) )
.previewInterfaceOrientation(.landscapeLeft)
}
}
34 changes: 34 additions & 0 deletions PlantUMLKeyboard/Sources/PlantUMLKeyboard/UIImage+Tiles.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// UIImage+Tiles.swift
//
//
// Created by Bartolomeo Sorrentino on 13/09/22.
//

import UIKit

// [StackOverflow](https://stackoverflow.com/a/73628496/521197)
extension UIImage {

func extractTiles(with tileSize: CGSize) -> [UIImage?] {

let hCount = Int(self.size.height / tileSize.height )
let wCount = Int(self.size.width / tileSize.width )

var tiles:[UIImage] = []

for i in 0...hCount-1 {
for p in 0...wCount-1 {
let rect = CGRect(
x: CGFloat(p) * tileSize.width,
y: CGFloat(i) * tileSize.height,
width: tileSize.width,
height: tileSize.height)
let temp:CGImage = self.cgImage!.cropping(to: rect)!
tiles.append(UIImage(cgImage: temp))
}
}
return tiles
}

}
25 changes: 25 additions & 0 deletions PlantUMLKeyboard/Sources/PlantUMLKeyboard/UIView+Responder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// File.swift
//
//
// Created by Bartolomeo Sorrentino on 13/09/22.
//

import UIKit

// [Get the current first responder without using a private API](https://stackoverflow.com/a/1823360/521197)
extension UIView {

var firstResponder: UIView? {
guard !isFirstResponder else { return self }

for subview in subviews {
if let firstResponder = subview.firstResponder {
return firstResponder
}
}

return nil
}
}

46 changes: 46 additions & 0 deletions PlantUMLKeyboard/Sources/PlantUMLKeyboard/Window+Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// File.swift
//
//
// Created by Bartolomeo Sorrentino on 13/09/22.
//

import UIKit


public func getKeyboardWindow() -> UIWindow? {
return UIApplication.shared.windows.last
/*
return UIApplication.shared.connectedScenes
// Keep only active scenes, onscreen and visible to the user
//.filter { $0.activationState == .foregroundActive }
// Keep only the first `UIWindowScene`
//.first(where: { $0 is UIWindowScene })
// Get its associated windows
.compactMap { $0 as? UIWindowScene }
.compactMap { $0.windows }
.flatMap { $0 }
.last
*/
}

public func getWindows() -> [UIWindow]? {


let scenes = UIApplication.shared.connectedScenes
guard let windowScene = scenes.first as? UIWindowScene else {
return nil
}
return windowScene.windows

}

func getFirstTextFieldResponder() -> UITextField? {

guard let firstWindow = getWindows()?.first, let firstResponder = firstWindow.firstResponder else {
return nil
}

return firstResponder as? UITextField
}

0 comments on commit ed1f8c1

Please sign in to comment.