【问题标题】:InputAccessoryView / View Pinned to Keyboard with SwiftUIInputAccessoryView / 使用 SwiftUI 固定到键盘的视图
【发布时间】:2019-11-18 08:09:18
【问题描述】:

在 SwiftUI 中是否有相当于 InputAccessoryView 的功能(或任何迹象表明即将到来?)

如果不是,您将如何模拟 InputAccessoryView 的行为(即固定在键盘顶部的视图)?所需的行为类似于 iMessage,其中有一个固定在屏幕底部的视图,当键盘打开时它会动画并位于键盘正上方。例如:

键盘关闭:

键盘打开:

【问题讨论】:

标签: swiftui


【解决方案1】:

iOS 15.0+

macOS 12.0+,Mac Catalyst 15.0+

ToolbarItemPlacement 在 iOS 15.0+ 中有一个新属性

keyboard

在 iOS 上,键盘项目在存在时位于软件键盘上方,或者在连接硬件键盘时位于屏幕底部。 在 macOS 上,键盘项目将放置在 Touch Bar 内。 https://developer.apple.com

struct LoginForm: View {
    @State private var username = ""
    @State private var password = ""
    var body: some View {
        Form {
            TextField("Username", text: $username)
            SecureField("Password", text: $password)

        }
        .toolbar(content: {
            ToolbarItemGroup(placement: .keyboard, content: {
                Text("Left")
                Spacer()
                Text("Right")
            })
        })
    }
}


iMessage 类似于 iOS 15+ 中的 InputAccessoryView。


struct KeyboardToolbar<ToolbarView: View>: ViewModifier {
    private let height: CGFloat
    private let toolbarView: ToolbarView
    
    init(height: CGFloat, @ViewBuilder toolbar: () -> ToolbarView) {
        self.height = height
        self.toolbarView = toolbar()
    }
    
    func body(content: Content) -> some View {
        ZStack(alignment: .bottom) {
            GeometryReader { geometry in
                VStack {
                    content
                }
                .frame(width: geometry.size.width, height: geometry.size.height - height)
            }
            toolbarView
                .frame(height: self.height)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}


extension View {
    func keyboardToolbar<ToolbarView>(height: CGFloat, view: @escaping () -> ToolbarView) -> some View where ToolbarView: View {
        modifier(KeyboardToolbar(height: height, toolbar: view))
    }
}

并像往常一样使用.keyboardToolbar 视图修饰符。


struct ContentView: View {
    @State private var username = ""
    
    var body: some View {
        NavigationView{
            Text("Keyboar toolbar")
                .keyboardToolbar(height: 50) {
                    HStack {
                        TextField("Username", text: $username)
                    }
                    .border(.secondary, width: 1)
                    .padding()
                }
        }
    }
}

【讨论】:

  • 我相信这个工具栏只有在输入字段被选中时才可见(键盘向上)。 OP 正在尝试模拟 iMessage 之类的聊天输入栏,如果没有出现键盘,该输入栏会粘在底部。
  • @Ferologics 检查我的答案。我刚刚找到了解决方案。
  • 您知道我们是否可以在使用UIViewRepresentable UITextField 创建自定义键盘时使用它吗?
【解决方案2】:

我有一个可以自定义工具栏的实现

public struct InputTextField<Content: View>: View {
    
    private let placeholder: LocalizedStringKey
    
    @Binding
    private var text: String
    
    private let onEditingChanged: (Bool) -> Void
    
    private let onCommit: () -> Void
    
    private let content: () -> Content
    
    @State
    private var isShowingToolbar: Bool = false
    
    public init(placeholder: LocalizedStringKey = "",
                text: Binding<String>,
                onEditingChanged: @escaping (Bool) -> Void = { _ in },
                onCommit: @escaping () -> Void = { },
                @ViewBuilder content: @escaping () -> Content) {
        self.placeholder = placeholder
        self._text = text
        self.onEditingChanged = onEditingChanged
        self.onCommit = onCommit
        self.content = content
    }
    
    public var body: some View {
        ZStack {
            TextField(placeholder, text: $text) { isChanged in
                if isChanged {
                    isShowingToolbar = true
                }
                onEditingChanged(isChanged)
            } onCommit: {
                isShowingToolbar = false
                onCommit()
            }
            .textFieldStyle(RoundedBorderTextFieldStyle())
            
            VStack {
                Spacer()
                if isShowingToolbar {
                    content()
                }
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    在 Swift Student 的 this post 的帮助下,我设法创建了一个运行良好的解决方案,其中包含很多您认为在 UIKit 中理所当然的功能的修改和添加。它是 UITextField 的一个包装器,但它对用户完全隐藏,而且它的实现非常 SwiftUI。您可以在我的GitHub repo 中查看它 - 您可以将它作为 Swift 包引入您的项目。

    (此答案中的代码太多,因此链接到repo)

    【讨论】:

    • 这看起来很有希望 - 谢谢!不幸的是,returnKey 选项不适用于numberPaddecimalPad。此外,似乎只有后退箭头可以让我在字段之间导航。有没有办法在工具栏而不是箭头和键盘上显示下一步/完成?
    • @mota 恐怕这是一项正在进行的工作,因此可能没有您想要的所有功能,而且可能存在错误,因为我无法彻底测试它 - 随意分叉并添加/修复东西
    • @mota 另外,我不相信 numberPads 和 decimalPads 能够显示返回键,因此使用工具栏(您可以在键盘关闭按钮和图标上设置操作)。该组件无意生成自定义键盘,它只是为可自定义的工具栏而构建的
    • @SomaMan OP 正在尝试模拟 iMessage 之类的聊天输入栏,该输入栏在关闭时停留在屏幕底部,而不是普通的输入附件视图。
    【解决方案4】:

    我已经在 iOS 14 上使用 99% 纯 SwiftUI 解决了这个问题。 在工具栏中,您可以显示任何您喜欢的视图。

    这是我的实现:

    import SwiftUI
    
     struct ContentView: View {
    
        @State private var showtextFieldToolbar = false
        @State private var text = ""
    
        var body: some View {
        
            ZStack {
                VStack {
                    TextField("Write here", text: $text) { isChanged in
                        if isChanged {
                            showtextFieldToolbar = true
                        }
                    } onCommit: {
                        showtextFieldToolbar = false
                    }
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                }
            
                 VStack {
                    Spacer()
                    if showtextFieldToolbar {
                        HStack {
                            Spacer()
                            Button("Close") {
                                showtextFieldToolbar = false
                                UIApplication.shared
                                        .sendAction(#selector(UIResponder.resignFirstResponder),
                                                to: nil, from: nil, for: nil)
                            }
                            .foregroundColor(Color.black)
                            .padding(.trailing, 12)
                        }
                        .frame(idealWidth: .infinity, maxWidth: .infinity,
                               idealHeight: 44, maxHeight: 44,
                               alignment: .center)
                        .background(Color.gray)   
                    }
                }
            }
        }
    }
    
     struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    【讨论】:

    • 很高兴看到一个最纯粹的 swiftUI 解决方案。或者,您也可以使用 onRecieve 来收听键盘通知发布者的键盘将显示/隐藏,而不是使用文本字段的提交和更改属性。您可以使用它来触发工具栏显示/隐藏更改。
    • #selector 是什么意思?
    • OP 正在尝试模拟 iMessage,例如在未显示键盘时会粘在底部的聊天输入栏。这只是一个常规的输入附件视图。
    【解决方案5】:

    我得到了一些非常接近想要的结果的工作。因此,起初,仅使用 SwiftUI 是不可能做到这一点的。您仍然必须使用 UIKit 来创建带有所需“inputAccessoryView”的 UITextField。 SwiftUI 中的 textfield 没有特定的方法。

    首先我创建了一个新结构:

    import UIKit
    import SwiftUI
    
    struct InputAccessory: UIViewRepresentable  {
    
        func makeUIView(context: Context) -> UITextField {
    
            let customView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
            customView.backgroundColor = UIColor.red
            let sampleTextField =  UITextField(frame: CGRect(x: 20, y: 100, width: 300, height: 40))
            sampleTextField.inputAccessoryView = customView
            sampleTextField.placeholder = "placeholder"
    
            return sampleTextField
        }
        func updateUIView(_ uiView: UITextField, context: Context) {
        }
    }
    

    这样我终于可以在我的视图主体中创建一个新的文本字段了:

    import SwiftUI
    
    struct Test: View {
        @State private var showInput: Bool = false
        var body: some View {
            HStack{
                Spacer()
                if showInput{
                    InputAccessory()
                }else{
                    InputAccessory().hidden()
                }
            }
        }
    }
    

    现在您可以隐藏和显示具有“showInput”状态的文本字段。下一个问题是,您必须在某个事件中打开键盘并显示文本字段。这对于 SwiftUI 来说又是不可能的,你必须回到 UiKit 并使其成为第一响应者。如果您尝试我的代码,您应该会在键盘上方看到红色背景。现在您只需将字段向上移动即可获得工作版本。

    总体而言,在当前状态下,无法使用键盘或某些文本字段方法。

    【讨论】:

      猜你喜欢
      • 2016-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 2019-07-21
      • 1970-01-01
      相关资源
      最近更新 更多