Cap*_*ril 9 alert textfield swiftui
有人知道如何在SwiftUI中创建一个包含TextField的Alert吗?
Art*_*uro 27
iOS 16+
struct ContentView: View {
@State private var presentAlert = false
@State private var username: String = ""
@State private var password: String = ""
var body: some View {
Button("Show Alert") {
presentAlert = true
}
.alert("Login", isPresented: $presentAlert, actions: {
TextField("Username", text: $username)
SecureField("Password", text: $password)
Button("Login", action: {})
Button("Cancel", role: .cancel, action: {})
}, message: {
Text("Please enter your username and password.")
})
}
}
Run Code Online (Sandbox Code Playgroud)
Mat*_*ini 22
Alert 目前相当有限,但您可以在纯 SwiftUI 中推出自己的解决方案。
这是带有文本字段的自定义警报的简单实现。
struct TextFieldAlert<Presenting>: View where Presenting: View {
@Binding var isShowing: Bool
@Binding var text: String
let presenting: Presenting
let title: String
var body: some View {
GeometryReader { (deviceSize: GeometryProxy) in
ZStack {
self.presenting
.disabled(isShowing)
VStack {
Text(self.title)
TextField(self.$text)
Divider()
HStack {
Button(action: {
withAnimation {
self.isShowing.toggle()
}
}) {
Text("Dismiss")
}
}
}
.padding()
.background(Color.white)
.frame(
width: deviceSize.size.width*0.7,
height: deviceSize.size.height*0.7
)
.shadow(radius: 1)
.opacity(self.isShowing ? 1 : 0)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
以及View使用它的扩展:
extension View {
func textFieldAlert(isShowing: Binding<Bool>,
text: Binding<String>,
title: String) -> some View {
TextFieldAlert(isShowing: isShowing,
text: text,
presenting: self,
title: title)
}
}
Run Code Online (Sandbox Code Playgroud)
演示:
struct ContentView : View {
@State private var isShowingAlert = false
@State private var alertInput = ""
var body: some View {
NavigationView {
VStack {
Button(action: {
withAnimation {
self.isShowingAlert.toggle()
}
}) {
Text("Show alert")
}
}
.navigationBarTitle(Text("A List"), displayMode: .large)
}
.textFieldAlert(isShowing: $isShowingAlert, text: $alertInput, title: "Alert!")
}
}
Run Code Online (Sandbox Code Playgroud)
tan*_*one 17
由于Alert提供的视图SwiftUI不能完成这项工作,您确实需要使用UIAlertControllerfrom UIKit。理想情况下,我们希望有一个TextFieldAlert观点,即我们能够在我们目前的以同样的方式呈现Alert提供SwiftUI:
struct MyView: View {
@Binding var alertIsPresented: Bool
@Binding var text: String? // this is updated as the user types in the text field
var body: some View {
Text("My Demo View")
.textFieldAlert(isPresented: $alertIsPresented) { () -> TextFieldAlert in
TextFieldAlert(title: "Alert Title", message: "Alert Message", text: self.$text)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以通过编写几个类并在View扩展中添加修饰符来实现这一点。
1)TextFieldAlertViewController创建一个UIAlertController(当然带有文本字段)并在它出现在屏幕上时呈现它。用户对文本字段的更改会反映到Binding<String>初始化期间传递的a中。
class TextFieldAlertViewController: UIViewController {
/// Presents a UIAlertController (alert style) with a UITextField and a `Done` button
/// - Parameters:
/// - title: to be used as title of the UIAlertController
/// - message: to be used as optional message of the UIAlertController
/// - text: binding for the text typed into the UITextField
/// - isPresented: binding to be set to false when the alert is dismissed (`Done` button tapped)
init(title: String, message: String?, text: Binding<String?>, isPresented: Binding<Bool>?) {
self.alertTitle = title
self.message = message
self._text = text
self.isPresented = isPresented
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Dependencies
private let alertTitle: String
private let message: String?
@Binding private var text: String?
private var isPresented: Binding<Bool>?
// MARK: - Private Properties
private var subscription: AnyCancellable?
// MARK: - Lifecycle
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentAlertController()
}
private func presentAlertController() {
guard subscription == nil else { return } // present only once
let vc = UIAlertController(title: alertTitle, message: message, preferredStyle: .alert)
// add a textField and create a subscription to update the `text` binding
vc.addTextField { [weak self] textField in
guard let self = self else { return }
self.subscription = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: textField)
.map { ($0.object as? UITextField)?.text }
.assign(to: \.text, on: self)
}
// create a `Done` action that updates the `isPresented` binding when tapped
// this is just for Demo only but we should really inject
// an array of buttons (with their title, style and tap handler)
let action = UIAlertAction(title: "Done", style: .default) { [weak self] _ in
self?.isPresented?.wrappedValue = false
}
vc.addAction(action)
present(vc, animated: true, completion: nil)
}
}
Run Code Online (Sandbox Code Playgroud)
2)使用协议TextFieldAlert包装TextFieldAlertViewController,UIViewControllerRepresentable以便它可以在 SwiftUI 中使用。
struct TextFieldAlert {
// MARK: Properties
let title: String
let message: String?
@Binding var text: String?
var isPresented: Binding<Bool>? = nil
// MARK: Modifiers
func dismissable(_ isPresented: Binding<Bool>) -> TextFieldAlert {
TextFieldAlert(title: title, message: message, text: $text, isPresented: isPresented)
}
}
extension TextFieldAlert: UIViewControllerRepresentable {
typealias UIViewControllerType = TextFieldAlertViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<TextFieldAlert>) -> UIViewControllerType {
TextFieldAlertViewController(title: title, message: message, text: $text, isPresented: isPresented)
}
func updateUIViewController(_ uiViewController: UIViewControllerType,
context: UIViewControllerRepresentableContext<TextFieldAlert>) {
// no update needed
}
}
Run Code Online (Sandbox Code Playgroud)
3)TextFieldWrapper是一个简单ZStack的TextFieldAlert,背面有 a (仅当isPresented为真)和正面的呈现视图。呈现视图是唯一可见的。
struct TextFieldWrapper<PresentingView: View>: View {
@Binding var isPresented: Bool
let presentingView: PresentingView
let content: () -> TextFieldAlert
var body: some View {
ZStack {
if (isPresented) { content().dismissable($isPresented) }
presentingView
}
}
}
Run Code Online (Sandbox Code Playgroud)
4)textFieldAlert修饰符允许我们将任何 SwiftUI 视图平滑地包装在 a 中TextFieldWrapper并获得所需的行为。
extension View {
func textFieldAlert(isPresented: Binding<Bool>,
content: @escaping () -> TextFieldAlert) -> some View {
TextFieldWrapper(isPresented: isPresented,
presentingView: self,
content: content)
}
}
Run Code Online (Sandbox Code Playgroud)
kon*_*iki 11
我发现 SwiftUI 中的模态和警报缺少几个功能。例如,似乎没有一种方法可以用 FormSheet 样式呈现模态。
当我需要呈现复杂的警报(例如带有文本字段的警报)时,我会创建一个包含警报所有内容的纯 SwiftUI 视图,然后使用UIHostController将其呈现为FormSheet。
如果你周围没有 UIViewController 来调用present(),你总是可以使用根视图控制器。
通过这种方法,您可以获得一些不错的功能,例如进出的标准警报动画。您也可以向下拖动警报以关闭它。
当键盘出现时,警报视图也会向上移动。
这在 iPad 上运行良好。在 iPhone 上,FormSheet 是全屏显示,因此您可能需要调整代码以找到解决方案。我认为这会给你一个很好的起点。
它是这样的:
struct ContentView : View {
@State private var showAlert = false
var body: some View {
VStack {
Button(action: {
let alertHC = UIHostingController(rootView: MyAlert())
alertHC.preferredContentSize = CGSize(width: 300, height: 200)
alertHC.modalPresentationStyle = UIModalPresentationStyle.formSheet
UIApplication.shared.windows[0].rootViewController?.present(alertHC, animated: true)
}) {
Text("Show Alert")
}
}
}
}
struct MyAlert: View {
@State private var text: String = ""
var body: some View {
VStack {
Text("Enter Input").font(.headline).padding()
TextField($text, placeholder: Text("Type text here")).textFieldStyle(.roundedBorder).padding()
Divider()
HStack {
Spacer()
Button(action: {
UIApplication.shared.windows[0].rootViewController?.dismiss(animated: true, completion: {})
}) {
Text("Done")
}
Spacer()
Divider()
Spacer()
Button(action: {
UIApplication.shared.windows[0].rootViewController?.dismiss(animated: true, completion: {})
}) {
Text("Cancel")
}
Spacer()
}.padding(0)
}.background(Color(white: 0.9))
}
}
Run Code Online (Sandbox Code Playgroud)
如果您发现自己经常使用它,按钮行可能会封装在一个单独的视图中,以便于重用。
Fab*_*tel 11
您可以直接使用UIAlertController。无需滚动您自己的警报对话框 UI:
private func alert() {
let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
alert.addTextField() { textField in
textField.placeholder = "Enter some text"
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in })
showAlert(alert: alert)
}
func showAlert(alert: UIAlertController) {
if let controller = topMostViewController() {
controller.present(alert, animated: true)
}
}
private func keyWindow() -> UIWindow? {
return UIApplication.shared.connectedScenes
.filter {$0.activationState == .foregroundActive}
.compactMap {$0 as? UIWindowScene}
.first?.windows.filter {$0.isKeyWindow}.first
}
private func topMostViewController() -> UIViewController? {
guard let rootController = keyWindow()?.rootViewController else {
return nil
}
return topMostViewController(for: rootController)
}
private func topMostViewController(for controller: UIViewController) -> UIViewController {
if let presentedController = controller.presentedViewController {
return topMostViewController(for: presentedController)
} else if let navigationController = controller as? UINavigationController {
guard let topController = navigationController.topViewController else {
return navigationController
}
return topMostViewController(for: topController)
} else if let tabController = controller as? UITabBarController {
guard let topController = tabController.selectedViewController else {
return tabController
}
return topMostViewController(for: topController)
}
return controller
}
Run Code Online (Sandbox Code Playgroud)
大部分代码只是样板文件,用于查找应显示警报的 ViewController。alert()例如从action按钮的调用:
struct TestView: View {
var body: some View {
Button(action: { alert() }) { Text("click me") }
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,beta 5 及更高版本中似乎存在一个错误,有时会导致模拟器在显示文本字段后冻结:Xcode 11 beta 5:将 textField 添加到 UIAlertController 时 UI 冻结
nig*_*ill 10
适用于 iOS 的简单本机解决方案
extension View {
public func textFieldAlert(
isPresented: Binding<Bool>,
title: String,
text: String = "",
placeholder: String = "",
action: @escaping (String?) -> Void
) -> some View {
self.modifier(TextFieldAlertModifier(isPresented: isPresented, title: title, text: text, placeholder: placeholder, action: action))
}
}
Run Code Online (Sandbox Code Playgroud)
public struct TextFieldAlertModifier: ViewModifier {
@State private var alertController: UIAlertController?
@Binding var isPresented: Bool
let title: String
let text: String
let placeholder: String
let action: (String?) -> Void
public func body(content: Content) -> some View {
content.onChange(of: isPresented) { isPresented in
if isPresented, alertController == nil {
let alertController = makeAlertController()
self.alertController = alertController
guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return
}
scene.windows.first?.rootViewController?.present(alertController, animated: true)
} else if !isPresented, let alertController = alertController {
alertController.dismiss(animated: true)
self.alertController = nil
}
}
}
private func makeAlertController() -> UIAlertController {
let controller = UIAlertController(title: title, message: nil, preferredStyle: .alert)
controller.addTextField {
$0.placeholder = self.placeholder
$0.text = self.text
}
controller.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
self.action(nil)
shutdown()
})
controller.addAction(UIAlertAction(title: "OK", style: .default) { _ in
self.action(controller.textFields?.first?.text)
shutdown()
})
return controller
}
private func shutdown() {
isPresented = false
alertController = nil
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
struct ContentView: View {
@State private var isRenameAlertPresented = false
@State private var title = "Old title"
var body: some View {
VStack {
Button("Rename title") {
isRenameAlertPresented = true
}
Text(title)
}
.textFieldAlert(
isPresented: $isRenameAlertPresented,
title: "Rename",
text: "Title",
placeholder: "",
action: { newText in
title = newText ?? ""
}
)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1589 次 |
| 最近记录: |