Swift UI Cách tạo TextField chỉ chấp nhận số


9

Tôi mới sử dụng swiftUI và iOs, tôi đang cố gắng tạo trường nhập liệu sẽ chỉ chấp nhận số

 TextField("Total number of people", text: $numOfPeople)

TextField cũng cho phép các ký tự chữ cái, làm cách nào để giới hạn người dùng chỉ nhập số?


Lưu ý rằng TextField có một meth methid lấy Formatter làm đối số
Joakim Danielson

@JoakimDanielson có lẽ bạn có thể hỗ trợ trong việc hiển thị cách tôi có thể sử dụng trình định dạng?
Lupyana Mbembati

Câu trả lời:


10

Bạn có thể đặt loại bàn phím trên TextFieldđó sẽ giới hạn những gì mọi người có thể gõ.

TextField("Total number of people", text: $numOfPeople)
    .keyboardType(.numberPad)

Tài liệu của Apple có thể được tìm thấy ở đây và bạn có thể xem danh sách tất cả các loại bàn phím được hỗ trợ tại đây .

Lưu ý: điều này sẽ không hoạt động trên iPad vì không có phím số cho iPad. Để có giải pháp tốt hơn, hãy kiểm tra giải pháp của John M bên dưới https://stackoverflow.com/a/58736068/5508175


1
Đây chính xác là những gì tôi đang tìm kiếm 👍
Lupyana Mbembati

Tôi đang gặp một vấn đề nhỏ, bàn phím sẽ không biến mất khi tôi gõ xong, bạn có biết tại sao không?
Lupyana Mbembati

1
@LupyanaMbembati câu hỏi / câu trả lời SO này có một số gợi ý về cách ẩn bàn phím sau khi bạn đã hoàn thành với nó. stackoverflow.com/questions/56491386/ Lời
Andrew

1
Xin lưu ý rằng điều này sẽ không hoạt động trên iPad
LuLuGaGa

3
Điều này không thực sự ngăn chặn đầu vào không phải là số; xem câu trả lời của tôi
John M.

23

Mặc dù hiển thị bàn phím số là bước đầu tiên tốt nhưng thực tế nó không ngăn được dữ liệu xấu được nhập vào:

  1. Người dùng có thể dán văn bản không phải số vào trường văn bản
  2. Người dùng iPad vẫn sẽ có được một bàn phím đầy đủ
  3. Bất cứ ai có bàn phím Bluetooth kèm theo đều có thể gõ bất cứ thứ gì

Những gì bạn thực sự muốn làm là vệ sinh đầu vào, như thế này:

import SwiftUI
import Combine

struct StackOverflowTests: View {
    @State private var numOfPeople = "0"

    var body: some View {
        TextField("Total number of people", text: $numOfPeople)
            .keyboardType(.numberPad)
            .onReceive(Just(numOfPeople)) { newValue in
                let filtered = newValue.filter { "0123456789".contains($0) }
                if filtered != newValue {
                    self.numOfPeople = filtered
                }
        }
    }
}

Bất cứ khi nào numOfPeoplethay đổi, các giá trị không phải là số được lọc ra và giá trị được lọc được so sánh để xem có numOfPeoplenên cập nhật lần thứ hai hay không, ghi đè đầu vào xấu bằng đầu vào được lọc.

Lưu ý rằng Justnhà xuất bản yêu cầu bạn import Combine.

BIÊN TẬP:

Để giải thích cho Justnhà xuất bản, hãy xem xét phác thảo khái niệm sau đây về những gì xảy ra khi bạn thay đổi giá trị trong TextField:

  1. Bởi vì TextFieldmất a Bindingđến a String, khi nội dung của trường được thay đổi, nó cũng ghi thay đổi đó trở lại @Statebiến.
  2. Khi một biến được đánh dấu @Statethay đổi, SwiftUI sẽ tính toán lại thuộc bodytính của chế độ xem.
  3. Trong quá trình bodytính toán, một Justnhà xuất bản được tạo ra. Combine có rất nhiều nhà xuất bản khác nhau phát ra các giá trị theo thời gian, nhưng Justnhà xuất bản chỉ lấy "chỉ" một giá trị duy nhất (giá trị mới numberOfPeople) và phát ra khi được hỏi.
  4. Các onReceivephương pháp làm cho một Viewthuê bao với một nhà xuất bản, trong trường hợp này, các Justnhà xuất bản, chúng tôi vừa tạo ra. Sau khi đăng ký, nó ngay lập tức yêu cầu bất kỳ giá trị có sẵn nào từ nhà xuất bản, trong đó chỉ có một giá trị mới numberOfPeople.
  5. Khi onReceivethuê bao nhận được một giá trị, nó thực hiện việc đóng được chỉ định. Đóng cửa của chúng tôi có thể kết thúc một trong hai cách. Nếu văn bản chỉ là số, thì nó không làm gì cả. Nếu văn bản được lọc là khác nhau, nó được ghi vào @Statebiến, bắt đầu lại vòng lặp, nhưng lần này việc đóng sẽ thực thi mà không sửa đổi bất kỳ thuộc tính nào.

Kiểm tra bằng cách sử dụng Kết hợp để biết thêm.


Tôi mới bắt đầu tìm hiểu về Swift / SwiftUI (đến từ thế giới Windows C # và Web Typecript), và vấn đề đầu tiên tôi gặp phải là làm thế nào để lọc đầu vào của người dùng để chỉ cho phép số. Rất phổ biến để lọc đầu vào dựa trên biểu thức regex. Giải pháp của bạn dường như chính xác là những gì tôi đang tìm kiếm - rất cảm ơn bạn. Tôi đã xem tài liệu và có vẻ hơi thiếu tốt nhất. Bạn có phiền giải thích lý do cho nhà xuất bản 'Chỉ'
Jesper Kristiansen

+1 cho câu trả lời này. Tuy nhiên, tôi không im lặng hiểu việc nhập Kết hợp cho điều đó. Chỉ là làm gì. Cảm ơn
davidev

@JesperKristiansen @davidev Tôi đã thêm một lời giải thích của Justnhà xuất bản.
John M.

Cảm ơn bạn đã giải thích tốt đẹp! Giải pháp của bạn hoạt động, nhưng trong một thời gian ngắn, bạn có thể thấy giá trị cũ trước khi nó được lọc. Có cách nào để ngăn chặn điều này?
Lupurus

1
Và nhân tiện: .onReceive được gọi mọi lúc, khi sth. khác trong Chế độ xem được thay đổi. Đây không phải là một chút quá nặng?
Lupurus

1

Bạn không cần sử dụng CombineonReceive, bạn cũng có thể sử dụng mã này:

class Model: ObservableObject {
    @Published var text : String = ""
}

struct ContentView: View {

    @EnvironmentObject var model: Model

    var body: some View {
        TextField("enter a number ...", text: Binding(get: { self.model.text },
                                                      set: { self.model.text = $0.filter { "0123456789".contains($0) } }))
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(Model())
    }
}

Thật không may, cũng có một nhấp nháy nhỏ, vì vậy bạn cũng có thể thấy các ký tự không được phép trong một thời gian rất ngắn (trong mắt tôi ngắn hơn một chút như cách với Combine)


0

Một cách tiếp cận khác có lẽ là tạo Chế độ xem bao bọc khung nhìn TextField và giữ hai giá trị: var riêng giữ Chuỗi đã nhập và giá trị ràng buộc giữ tương đương Double. Mỗi lần người dùng nhập một ký tự, nó sẽ cố cập nhật Double.

Đây là một triển khai cơ bản:

struct NumberEntryField : View {
    @State private var enteredValue : String = ""
    @Binding var value : Double

    var body: some View {        
        return TextField("", text: $enteredValue)
            .onReceive(Just(enteredValue)) { typedValue in
                if let newValue = Double(typedValue) {
                    self.value = newValue
                }
        }.onAppear(perform:{self.enteredValue = "\(self.value)"})
    }
}

Bạn có thể sử dụng nó như thế này:

struct MyView : View {
    @State var doubleValue : Double = 1.56

    var body: some View {        
        return HStack {
             Text("Numeric field:")
             NumberEntryField(value: self.$doubleValue)   
            }
      }
}

Đây là một ví dụ cơ bản - bạn có thể muốn thêm chức năng để hiển thị cảnh báo cho đầu vào kém và có thể kiểm tra giới hạn, v.v ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.