こんにちはフリーランスの永田です。最近は法人化の手続きを開始しました。
SwiftUI案件を1月から実施予定で、現在技術を調査中です。
今回はキャレット動作 returnButtonを押下しましたら、水平移動する対応です。
環境
Xcode 11.3
SwiftUI
SwiftUIではない場合(オリジナルです。)
キャレット動作しない場合
キャレット動作
SwiftUI_UITexTField_CaretLogic
— DaisukeNagata (@dbank0208) December 17, 2019
情報が少ないテクニック。returnボタンを押下すると、カーソルの移動 by SwiftUIhttps://t.co/h8EMGgS8za pic.twitter.com/ehsFV5qoRj
import UIKit
import SwiftUI
struct ContentView: View {
@State var text: String = ""
@State var text2: String = ""
@State var spacing: CGFloat = 0
@State var didTap = false
var body: some View {
VStack {
HStack(alignment: .bottom, spacing: spacing) {
SATextField(tag: 0, placeholder: "placeholder", changeHandler: { (newString) in
self.text = newString
}, onCommitHandler: {
// write something
})
self.text.isEmpty == false ?
HorizontalLine(color: self.didTap ? Color.red : Color.black) :
HorizontalLine(color: self.didTap ? Color.black : Color.red)
SATextField(tag: 1, placeholder: "placeholder2", changeHandler: { (newString) in
self.text2 = newString
}, onCommitHandler: {
// write something
})
text2.isEmpty == false ?
HorizontalLine(color: didTap ? Color.red : Color.black) :
HorizontalLine(color: didTap ? Color.black : Color.red)
}
}.position(.init(x: UIScreen.main.bounds.width/2+50, y: UIScreen.main.bounds.height/2))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct HorizontalLine: View {
private var color: Color? = nil
private var height: CGFloat = 1.0
private var shape: HorizontalLineShape?
init(color: Color, height: CGFloat = 1.0) {
self.color = color
self.height = height
}
var body: some View {
HorizontalLineShape().fill(self.color!).frame(minWidth: 0, maxWidth: .infinity, minHeight: height, maxHeight: height)
}
}
struct HorizontalLineShape: Shape {
func path(in rect: CGRect) -> Path {
let fill = CGRect(x: -rect.size.width, y: 0, width: rect.size.width, height: rect.size.height)
var path = Path()
path.addRoundedRect(in: fill, cornerSize: CGSize(width: 2, height: 2))
return path
}
}
class Model: ObservableObject {
@Published var text = ""
var placeholder = "Placeholder"
}
// check is this
// https://medium.com/@valv0/textfield-and-uiviewrepresentable-46a8d3ec48e2
struct SATextField: UIViewRepresentable {
private let tmpView = WrappableTextField()
//var exposed to SwiftUI object init
var tag:Int = 0
var placeholder:String?
var changeHandler:((String)->Void)?
var onCommitHandler:(()->Void)?
func makeUIView(context: UIViewRepresentableContext<SATextField>) -> WrappableTextField {
tmpView.tag = tag
tmpView.delegate = tmpView
tmpView.placeholder = placeholder
tmpView.onCommitHandler = onCommitHandler
tmpView.textFieldChangedHandler = changeHandler
return tmpView
}
func updateUIView(_ uiView: WrappableTextField, context: UIViewRepresentableContext<SATextField>) {
uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
uiView.setContentHuggingPriority(.defaultLow, for: .horizontal)
}
}
class WrappableTextField: UITextField, UITextFieldDelegate {
var textFieldChangedHandler: ((String)->Void)?
var onCommitHandler: (()->Void)?
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = textField.superview?.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let currentValue = textField.text as NSString? {
let proposedValue = currentValue.replacingCharacters(in: range, with: string)
textFieldChangedHandler?(proposedValue as String)
}
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
onCommitHandler?()
}
}
参考サイト
こちらのロジックを拝借させていただきました。ロジックの部分は、今まで通りのプログラムになります。
https://medium.com/@valv0/textfield-and-uiviewrepresentable-46a8d3ec48e2
プログラムがわかる場合は
changeHandler
を追えば、すぐにわかると思います
起動時に
makeUIView
メソッドでtmpView.textFieldChangedHandler = changeHandler
を代入します。
既存のUITextFieldメソッドで実装しています。onCommitHandler
もbind処理をしていますが、
Flowは同じです。
このメソッドは文字が1文字づつ変化する事に処理が行われます。 textFieldChangedHandler?(proposedValue as String)
でbindしてchangeHandler
が呼ばれています。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let currentValue = textField.text as NSString? {
let proposedValue = currentValue.replacingCharacters(in: range, with: string)
textFieldChangedHandler?(proposedValue as String)
}
return true
}
return Buttonを押下時 これでキャレットnextField.becomeFirstResponder()
UITextFieldのキーボードを開くメソッドです。
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = textField.superview?.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
以上、とても簡単に解説しました
貴重なお時間お読みくださいまして、誠にありがとうございます。