- 投稿日:2020-03-22T23:38:03+09:00
SwiftUI の Property Wrappers
Property Wrappers
SwiftUIのpropertyWrapperの前に、そもそもpropertyWrapperとは何かを説明させてください。
変数をラッピングして、アクセス(get/set)を制御するための仕組みです。
簡単な例を見てみましょう。@propertyWrapper struct HelloWorld { private var text: String init() { text = "" } var wrappedValue: String { get { return text } set { if newValue == "世界" { text = "こんにちは, \(newValue)!" return } text = "Hello, \(newValue)!" } } }クラスや構造体、enumに
@peopertyWrapper
をつけることで定義できます。
定義したら、それを以下のように変数に適用することができます。@HelloWorld var name: String
name
に何らかの値をセットすると、"Hello, (name)!"という値に変換してセットされるようになります。日本語で"世界"とセットしたときだけ、"こんにちは, 世界!"となるようにしてみました。wrappedValue
propertyWrapperはラッピングした値を返す変数である
wrappedValue
を実装する必要があります。この値のget/setで、ラッピングした値を変更したりします。
projectedValue
追加でprojectedValueという変数を実装することもできます。ラッピングした値の状態を示したり、
wrappedValue
に関するなんらかの情報を返すことができます。外からこの値にアクセスするには$
を頭につけなければいけません。例えば、@propertyWrapper struct HelloWorld { var projectedValue: Bool init() { projectedValue = false } var wrappedValue { // ... get .. set { if newValue == "世界" { text = "こんにちは, \(newValue)!" projectedValue = true return } text = "Hello, \(newValue)!" projectedValue = false } } }とすると、
$name
で、Bool値が返ります。class Hello { @HelloWorld(text: "Hello, World!") var str: String func sayHello() { print(str) // Hello, World! print("\($str)") // false str = "世界" print(str) // こんにちは, 世界! print("\($str)") // true } }「世界」という値をセットしたときだけ、
projectedValue
がtrue
になるようにしています。
このようにprojectedValue
を使ってwrappedValue
の追加情報(投影値?)を返すことができます。SwiftUI の propertyWrapper
以降はSwiftUIで定義されたpropertyWrapperについてみていこうと思います。
State
SwiftUIのviewは構造体(値)のため、変更不可です。ですが、変数に
@State
をつけると、その変数に変更が入ったら自動でviewを再構築してくれます。なのでviewで変更する必要があるものは@State
をつけます。struct GreetingView: View { @State var message: String = "" var body: some View { VStack { Text(message) } } }
State
はprojecteValue
として、後述するBinding
を返します。上記の例だと、$message
で、String
をBinding
でラップした値を返してくれます。
Binding
双方向データバインディングというやつです。
Binding
の値を変更すると、その情報源(変数)に、値の変更を反映してくれます。逆に情報源を更新すれば、Binding
から取得できる値も更新されています。
State
のprojectedValue
($)は、自身の値と結びつくBinding
を返してくれるので、自身の値を更新してほしいときに渡します。struct GreetingView: View { @State var message: String = "ボタンは押さないでね★" var body: some View { VStack { Text(message) // Bindingする例 GreetingButton(message: $message) // Bindingしない例 StateGreetingButton(message: message) } } } struct GreetingButton: View { @Binding var message: String var body: some View { Button(action: { self.message = "ボタン...押しましたね..." }) { Text("絶対に押すな!") } } } struct StateGreetingButton: View { @State var messageState: String var body: some View { Button(action: { self.messageState = "Bindingではないです" }) { Text("押してもいいですよ") } } }
GreetingButton
で、Binding
であるmessage
を更新すると、情報源であるGreetingView
のmessage
に変更が反映されます。
しかし、StateGreetingButton
のボタンを押して、messageState
を変更しても、情報源であるGreetingView.message
には反映されません。もう一つの例をあげると、
Toggle
の引数isOn
にBinding
を渡しますよね。これは、Toggle
自身がisOn
に渡したBinding
の情報源を変更するためです。@State var onOff: Bool = true var body: some View { Toggle(isOn: $onOff) { Text("情報源(var onOff)の値を変えますよ: \(onOff.description)") } }ObservedObject
ObservedObject
はObservableObject
を実装したクラスをwrappedValue
として持ちます。struct PlayerViewView: View { @ObservedObject var user: PlayerViewModel = PlayerViewModel() var body: some View { VStack { Text("あなたは \(user.age) 歳です") Button(action: { self.user.haveBirthday() }) { Text("何歳ですか?") } } } } final class PlayerViewModel: ObservableObject { private(set) var age: Int = 0 { willSet { objectWillChange.send() } } func haveBirthday() { age += 1 } }上記の例だと、ボタンを押すたびに、
PlayerView
へPlayerViewModel
の変更が通知され、viewが更新されます。
データフローは@State
と変わらないのですが、@ObservedObject
の場合は、wrappedValue
がObservableObject
のオブジェクトでないといけない、という点が異なります。ObservableObject
ObservableObject
はCombine
フレームワークに含まれるプロトコルです。オブジェクトの変更を通知できることを表します。objectWillChange
というPublisher(変更イベントの発行元)を持っており、これを購読していれば、ObservableObject
の変更を検知できます。
ObservableObject
が自身に変更が入った、ということを通知するには、以下のようにobjectWillChange.send()
を実行します。class SomeObservableObject: ObservableObject { var hello: String { willSet { // ObservableObjectに適合するクラスは objectWillChangeを持っている objectWillChange.send() } } }以下のように購読すれば、変更が入ったことを検知できます。
class Hoge { var cancellable: AnyCancellable? init() { cancellable = someObservalbeObject.objectWillChange.sink { _ in // objectWillChange.send() が呼ばれた! } } }
@ObservedObject
のpropertyWrapperを使うと、SwiftUIがObservableObject
を購読し、変更が入るたびにviewを更新してくれます。
Published
SwiftUIではなく、Combineで定義されたpropertyWrapperですが、
ObservableObject
でよく使われるので説明します。
変数に@Published
を付与すると、その変数のprojectedValue
は自身の変更を通知するPublisher
を返してくれます。class Dog { @Published var name: String = "Pero" private var cancellable: AnyCancellable? init() { self.cancellable = $name.sink { newName in print("あなたの名前: \(newName)") } } } let dog = Dog() // あなたの名前: Pero dog.name = "John" // あなたの名前: John dog.name = "Taro" // あなたの名前: Taro
sink
で購読し、値の変更を監視しています。変更が入るたびにログ出力しています。
ObservableObject
というプロトコルに適合したクラスで、@Published
の変数を持っている場合、それに対して変更すると、ObservableObject
の変更を自動で通知してくれます。class Dog: ObservableObject { @Published var name: String = "Pero" private var cancellable: AnyCancellable? init() { self.cancellable = objectWillChange.sink { print("名前、変わりましたね...") } } } let dog = Dog() dog.name = "John" // 名前、変わりましたね...なので、
@Published
を使えば、自前でwillSet
にてobjectWillChange.send()
なんて書かずによいので楽です。EnvironmentObject
ObservableObject
を子ビューでも使いまわしたい場合は、@ObservedObject
ではなく、@EnvironmentObject
を使用します。struct ContentView: View { @EnvironmentObject var setting: PlayerSettings var body: some View { VStack { Text("あなたの通知設定: \(setting.isNotificationEnable.description)") SubView() } } } struct SubView: View { @EnvironmentObject var setting: PlayerSettings var body: some View { VStack { Button(action: { self.setting.isNotificationEnable.toggle() }) { Text("通知設定を変更") } } } } /// View間で共有するオブジェクト final class PlayerSettings: ObservableObject { @Published var isNotificationEnable: Bool = false }上記の例だと、
SubView
のsetting
は親View(ContentView
)のsetting
が自動でインジェクトされます。なので、子View側で、変更したら、親Viewにも反映されます。ただし、最初に
@EnvironmentObject
を使用しているViewの生成時に、オブジェクトを生成して渡す必要があります。渡さないとクラッシュします。let contentView = ContentView().environmentObject(PlayerSettings())もう一つ注意する点は、
sheet
などで表示した親子関係でない別のViewには自動でインジェクトされないということです。共有したい場合は、明示的に渡す必要があります。struct ContentView: View { @EnvironmentObject var setting: PlayerSettings @State var showModal: Bool = false var body: some View { VStack { Text("あなたの通知設定: \(setting.isNotificationEnable.description)") Button(action: { self.showModal = true }) { Text("べつのがめん") }.sheet(isPresented: $showModal) { // 親子関係でないViewでも共有したい場合は、明示的に渡す必要がある SubView().environmentObject(self.setting) } } } }Environment
SwiftUIで定義されたViewの設定値を取得することができます。
struct SubView: View { @Environment(\.isEnabled) var enable: Bool var body: some View { VStack { Text("活性状態? \(enable.description)") } } }例えば上記画面の表示時に、以下のように
disabled(true)
とすれば、@Environment(\.isEnabled)
はfalse
になります。SubView().disabled(true)
- 投稿日:2020-03-22T23:27:58+09:00
【Swift】クロージャの中に書く[weak self] はじめからていねいに
はじめに
少し前に業務の中でクロージャを丁寧に実装する必要があり、その際に調べたものを文字残して、しっかりとまとめておこうと思いました。この記述が必要である理由を説明するには色々な知識が必要になりますが、この記事の結論を1行でまとめると
「
[weak self]
と記述することでクロージャがself
を弱参照し、強参照による循環を防ぐ」になります。以下で順を追ってこのテーマについてまとめます。
強参照の循環
強参照とは
強参照 (strong reference)とはあるオブジェクトが他のオブジェクトを参照する際のデフォルトの参照方法です。Swiftでは、ARC (Auto Reference Counting) と呼ばれる仕組みで、オブジェクトが他のオブジェクトから参照されている数 (参照カウント)を記憶して、メモリ領域内のオブジェクトの管理を行います。オブジェクトの参照カウントが1以上の場合はそのオブジェクトは破棄されず、参照カウントが0になったとき、ガベージコレクションの要領でそのオブジェクトは破棄されます。
Swiftにおいて、あるオブジェクトが他のオブジェクトを参照した場合、参照した側は参照された側を強参照で参照したものとして処理されます。そして、ARCでは強参照で参照されたとき、そのオブジェクトの参照カウントは1として計上されます。
例
class Hoge {} var h: Hoge? = Hoge() //(1) h = nil //(2)強参照の循環
他のプログラミング言語でも起こりうるように、Swiftでも参照型のオブジェクト同士では強参照による循環 (strong reference cycle)が起こり得ます。
class Hoge { var fuga: Fuga? } class Fuga { var hoge: Hoge? } var h: Hoge? = Hoge() var f: Fuga? = Fuga() //(1) h.fuga = f f.hoge = h //(2) h = nil f = nilこのような循環参照が発生した場合、
Hoge()
とFuga()
のは参照カウントが1になったまま、各インスタンスへ変数からアクセスができなくない状態になったため、それぞれのインスタンスが解放されない状態となってしまっているのがわかります。クロージャで起こりうる循環参照
上記のような循環参照はクロージャとあるクラスのインスタンスの間でも起こり得ます。この記事のタイトルにもなっている
[weak self]
はクロージャの中に記述するキャプチャリストの記法の1つです。以下の例を見てください。class Hoge { private var closure: (() -> Void)? private var count = 0 init() { closure = createClosure() } func createClosure() -> (() -> Void) { return { [self] in self.count += 1 } } } var h: Hoge? = Hoge() //(1) h = nil //(2)この例を実行した場合、
hoge
が生成された時点でイニシャライザ内部でcreateClosure()
が呼び出され、クロージャ{ [self] in self.count += 1 }
のインスタンスが生成され、プロパティclosure
に格納され1、self
→クロージャへの参照が発生します。そして、このクロージャは内部でself
を参照しているため、クロージャ→self
への強参照が発生します。すると、h
と内部で生成されるクロージャのインスタンスは互いに強参照で参照しあっているため、参照の循環が発生していることがわかります。弱参照による解決
上記の問題を解決するために、参照型のインスタンスを参照する際にSwiftでは強参照ではなく、弱参照(weak reference)という方法でインスタンスを参照する方法があります。弱参照による参照を受けた場合、ARCの参照カウントには含まれず、他のオブジェクトから参照されながらであってもメモリ領域を解放することができます。
これをクロージャのキャプチャリストに適用すると、記事のタイトルにある
[weak self]
の記法になります。これにより、先に説明したクロージャとインスタンスの強循環参照を無くし、インスタンスを解放することができるようになります。したがって、冒頭でも提示したように[weak self]
とすることによって、[weak self]
と記述することでクロージャがself
を弱参照し、強参照による循環を防ぐということになっています。class Hoge { private var closure: (() -> Void)? private var count = 0 init() { closure = createClosure() } func createClosure() -> (() -> Void) { return { [weak self] in self.count += 1 } } } var h: Hoge? = Hoge() //(1) h = nil //(2)参考
詳解 Swift 第5版
- 投稿日:2020-03-22T20:03:10+09:00
「画面遷移で画面が浮く」を解決
開発環境
Xcode: Version 11.3.1
記事記入時: 2020/03/22解決したい問題
・以前までは,画面遷移がうまくできていたのに!
・動画等,教材通りにならない!↑のように画面遷移を実装した際に,
遷移後の画面が浮いたみたいになってる......ViewController.swiftoverride func viewWillAppear(_ animated: Bool) { ~ ~ }が正常に作動しない.
解決方法
画面遷移では
・「control」+ドラッグ
・Present Modally
で実装する場合について書いていこうと思います!この際に,Transitionを指定してやることで解決できました.
まずは,storyboard上のsegueを選択します.
そして,右の野球ベースみたいなところを選択すると,
PresentationがSame As Destinationになってるかと思います.
このデフォルト設定を Full Screen にしてやることで無事解決しました!すると!
そして,,,
ViewController.swiftoverride func viewWillAppear(_ animated: Bool) { ~ ~ }が作動しない問題も,Presentationがデフォルトだと画面が遷移してない状態なので,作動していなかったのです!
PresentationをFull Screenにすることでこの問題も無事解決しました!
これで無事画面遷移を実装できましたね!
- 投稿日:2020-03-22T20:03:10+09:00
「画面遷移で画面が浮く」を解決(swift_xcode)
開発環境
Xcode: Version 11.3.1
記事記入時: 2020/03/22解決したい問題
・以前のバージョンまでは,画面遷移がうまくできていたのに!
・動画・記事等,教材通りにならない!↑のように画面遷移を実装した際に,
遷移後の画面が浮いたみたいになってる......ViewController.swiftoverride func viewWillAppear(_ animated: Bool) { //code記述 }が正常に作動しない.
解決方法
画面遷移では
・「control」+ドラッグ
・Present Modally
で実装する場合について書いていこうと思います!この際に,Transitionを指定してやることで解決できました.
まずは,storyboard上のsegueを選択します.
そして,右の野球ベースみたいなところを選択すると,
PresentationがSame As Destinationになってるかと思います.
このデフォルト設定を Full Screen にしてやることで無事解決しました!すると!
見事,画面遷移後もスクリーンがきれいになりました!
そして,,,
ViewController.swiftoverride func viewWillAppear(_ animated: Bool) { //機能 }が作動しない問題も,Presentationがデフォルトだと画面が遷移してない状態なので,作動していなかったのです!
PresentationをFull Screenにすることでこの問題も無事解決しました!
これで無事画面遷移を実装できましたね!
- 投稿日:2020-03-22T19:04:40+09:00
Swiftで簡単なコードを書いてみた(初心者向け)
はじめに
アプリ開発に最近興味を持ち始めSwiftについて勉強したことのアウトプットとして書きました
初心者向けの簡単な文法や記述方法について触れていますSwiftって?
誰もが圧倒的に優れたアプリケーションを作れる、パワフルなオープンソースの言語です(Appleサイトより)
主に下記アプリの基幹言語として使用されています
・iPhoneアプリ
・OSXアプリ
・Apple Watchアプリ
・tvOSアプリ環境構築
WindowsやLinuxでもSwiftは動かすことは出来ますが、やはりMacを使用するのが適しています
また、実際に開発を行うのは「XCode」というソフトを使用します詳しくは下記サイトで環境構築をしてみてください
XcodeとSwiftを使ってアプリ開発を体験してみよう実装
Xcodeをインストール後、以下の画面が表示されるので、今回はplaygroudを使用していきます
playgroudは名の通り、遊び場のように作ってはすぐに実装の確認をすることが出来ます
Single Viewを選択し適当な名前を付けます
初期の起動時に、自動的にHello Worldを出力するソースがあるので、とりあえず実行してみました
実装の確認を同一の画面で確認出来ることが分かると思います
簡単な記述方法
それでは簡単なコードをSwiftで記述していきます
1.文字の出力
print("Hello Swift") //実行結果 //Hello Swiftと記述することでテキスト出力します(改行あり)
Java等に比べると、シンプルに記述可能です2.if文
var num = 1 if num < 10 { print("10より小さい値です") }else{ print("10より大きい値です") } //実行結果 //10より小さい値ですvarで変数を定義し、if文で判定を行っています
ちなみに、変数numにカーソルを当てQuick Helpというツールを使うと自動的に変数の型を明示してくれます。これも便利ですね
3.for文
for n in 1...5 { print(n) } //実行結果 //1 //2 //3 //4 //51から5までの繰り返しを記述します
こちらも独特な書き方ですが、分かりやすいです4.while文
var cnt = 0 var sum = 0 while cnt < 5{ cnt += 1 sum += cnt } print("cnt:\(cnt)") print("sum:\(sum)") //実行結果 //cnt:5 //sum:15終わりに
ここまで読んで頂きありがとうございました
今回は簡単なSwiftの記述方法に触れましたが、他の言語と比べ書き方が相違する所はありますが、簡略して書けるのではと思います
これからもSwiftについて勉強していこうと思います
- 投稿日:2020-03-22T18:04:38+09:00
How to show view controller in full screen
Q:
when I open a view controller in following way, it don't be shown in full screenlet viewController = UIViewController()
present(viewController, animated: true, completion: nil)A:
just add the following code.
let viewController = UIViewController()
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: true, completion: nil)
- 投稿日:2020-03-22T10:50:33+09:00
[Swift] Class-only Protocolは": class"じゃなくて": AnyObject"で宣言することに慣れましょう
概要
本記事の前提環境:Swift 5.1
ちょっと今さら感があるネタですが。。。
Class-only Protocolは、
↓こうじゃなくてprotocol MyDelegate: class { }↓こう宣言しましょう、という話題です。
protocol MyDelegate: AnyObject { }私なりに知ってはいたのですがメンバーに理由をうまく説明できなかったので、今さらながら整理してみました。
前置き
Xcodeでこのような実装をするとエラーになるので、こういう実装はできないのだな、ということにすぐに気づくと思います。
protocol MyDelegate { } class MyClass { weak var delegate: MyDelegate? }
weak
は循環参照を避けるために「弱参照にします」というキーワードであって、参照型の変数じゃないと意味がないため、myDelegate
を参照型だけに適用できるような宣言をしないとダメですよ、という訳です。本題
Swift 3までは、このような↓書き方をしていました。
protocol MyDelegate: class { }Swift 4で、SE-0156という提案に基づいて、
AnyObject
が導入されました。
以降は、こちら↓の書き方が推奨となっています。protocol MyDelegate: AnyObject { }参考:Swift Documentation – Class-only Protocols
SE-0156の背景と意図は私には完璧に読み取り難いですが、
AnyObject
とは「参照型全般」という意味を明確にしたモノ、という理解です。Swift 4以降、
class
は後方互換のためにAnyObject
のtypealias(別名)として残っている状態です。結論
Swift 5.1現在では、
: class
で宣言してもエラーになりませんし警告も出ません。
また、ビルドや実行時に遅くなるということもないようです。ただし、SE-0156には以下のような記述もあり、将来はdeprecatedとして警告が発生する可能性があります。
Later,
class
could be removed in a subsequent version of Swift.このため、コードに
: class
を見かけたら、: AnyObject
にリファクタリングしておいた方が良さそうです。
- 投稿日:2020-03-22T10:50:11+09:00
[iOS] [Xcode] WKWebViewで位置情報を取得するコンテンツをloadする
前提環境
- Xcode 11.3
- Swift 5.1
info.plistの設定
謎の挙動
許諾アラートで「OK」をtapして、WKWebViewをいったん閉じて、再度WKWebViewを開いてもまた許諾アラートが表示されます。
2回目の「OK」で記憶されて、3回目は表示されなくなるようです。
この挙動の原因と回避方法は謎です。。。どなたかご存知でしたら教えてください。注意点
Xcode 11.3で新規作成したプロジェクトでは、コンテンツが位置情報を取得しようとした際にコンソールに以下のログが出力されてクラッシュします。
*** Terminating app due to uncaught exception 'NSObjectNotAvailableException', reason: 'UIAlertView is deprecated and unavailable for UIScene based applications, please use UIAlertController!'許諾アラートが表示されるタイミングで、Xcode 11で新規にプロジェクトを作成した時のデフォルトである「scene baseアプリケーション」の場合にクラッシュしてしまうようです。
許諾アラートはアプリ側で実装している訳ではないので、どうやらSDKのバグっぽい雰囲気です。
Xcode 11.3現在では、window baseアプリケーションにするしかなさそうです。1.SceneDelegate.swiftをファイルごと削除
2.AppDelegate.swiftを修正
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? // この行を追加 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } // ここから下を削除 // // MARK: UISceneSession Lifecycle // // func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // // Called when a new scene session is being created. // // Use this method to select a configuration to create the new scene with. // return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) // } // // func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { // // Called when the user discards a scene session. // // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // // Use this method to release any resources that were specific to the discarded scenes, as they will not return. // } }3.info.plistから"Application Scene Manifestを削除
この件については、Xcodeがバージョンアップされたら再確認しようと思います。
- 投稿日:2020-03-22T04:23:56+09:00
Delagateを用いたキーボードの閉じ方
TextFieldを開いた時に展開されるキーボードを、余白のタッチやリターンキー(キーボードの左下にある)を押して閉じる方法。
全体像
qiita.rbimport UIKit class ViewController: UIViewController,UITextFieldDelegate { @IBOutlet weak var logoImageView: UIImageView! @IBOutlet weak var userNameTextField: UITextField! @IBOutlet weak var passWordTextField: UITextField! @IBOutlet weak var userNameLabel: UILabel! @IBOutlet weak var passWordLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() //以下二つのテキストフィールドに、returnでキーボードを閉じることができるメソッドが入ったクラスを設定する = Delegate //よって、これらに対応したキーボードのみ、returnで閉じることができる userNameTextField.delegate = self passWordTextField.delegate = self } @IBAction func login(_ sender: Any) { logoImageView.image = UIImage(named: "loginOK") userNameLabel.text = userNameTextField.text passWordLabel.text = passWordTextField.text } //タッチでキーボードを閉じる //タッチした時に呼ばれるメソッド override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) //編集を終了してもよろしいですか?で消える } //リターンキーを押した時にキーボードを閉じる func textFieldShouldReturn(_ textField: UITextField) -> Bool { //キーボードが閉じる textField.resignFirstResponder() return true //Boolだからtrueかfalseを返さないといけない } }まずはDelegateクラスを定義する
分割して説明すると、UITextFieldDelegateクラスを定義してその中のメソッドの使用を可能にして、
qiita.rbimport UIKit class ViewController: UIViewController,UITextFieldDelegate {そのDelegateを、反映させたいTextFieldに設定する。
(このTextField上のキーボードでは、のちに設定するDelegateクラスのメソッドが反映される)qiita.rboverride func viewDidLoad() { super.viewDidLoad() userNameTextField.delegate = self passWordTextField.delegate = self余白のタッチでキーボードを閉じる
touchesBeganメソッドは、余白をタッチした時に呼ばれるメソッド。その関数の中に、view.endEditing(true) つまりエディター(キーボード)をエンド(終える)すると書いて終わり。
qiita.rboverride func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) }リターンキーのタップでキーボードを閉じる
textFieldShouldReturnメソッドは、リターンキーをタップした時に呼ばれるメソッド。
qiita.rbfunc textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true }
- 投稿日:2020-03-22T04:23:56+09:00
Delegateを用いたキーボードの閉じ方
TextFieldを開いた時に展開されるキーボードを、余白のタッチやリターンキー(キーボードの左下にある)を押して閉じる方法。
全体像
qiita.rbimport UIKit class ViewController: UIViewController,UITextFieldDelegate { @IBOutlet weak var logoImageView: UIImageView! @IBOutlet weak var userNameTextField: UITextField! @IBOutlet weak var passWordTextField: UITextField! @IBOutlet weak var userNameLabel: UILabel! @IBOutlet weak var passWordLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() //以下二つのテキストフィールドに、returnでキーボードを閉じることができるメソッドが入ったクラスを設定する = Delegate //よって、これらに対応したキーボードのみ、returnで閉じることができる userNameTextField.delegate = self passWordTextField.delegate = self } @IBAction func login(_ sender: Any) { logoImageView.image = UIImage(named: "loginOK") userNameLabel.text = userNameTextField.text passWordLabel.text = passWordTextField.text } //タッチでキーボードを閉じる //タッチした時に呼ばれるメソッド override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) //編集を終了してもよろしいですか?で消える } //リターンキーを押した時にキーボードを閉じる func textFieldShouldReturn(_ textField: UITextField) -> Bool { //キーボードが閉じる textField.resignFirstResponder() return true //Boolだからtrueかfalseを返さないといけない } }まずはDelegateクラスを定義する
分割して説明すると、UITextFieldDelegateクラスを定義してその中のメソッドの使用を可能にして、
qiita.rbimport UIKit class ViewController: UIViewController,UITextFieldDelegate {そのDelegateを、反映させたいTextFieldに設定する。
(このTextField上のキーボードでは、のちに設定するDelegateクラスのメソッドが反映される)qiita.rboverride func viewDidLoad() { super.viewDidLoad() userNameTextField.delegate = self passWordTextField.delegate = self余白のタッチでキーボードを閉じる
touchesBeganメソッドは、余白をタッチした時に呼ばれるメソッド。その関数の中に、view.endEditing(true) つまりエディター(キーボード)をエンド(終える)すると書いて終わり。
qiita.rboverride func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) }リターンキーのタップでキーボードを閉じる
textFieldShouldReturnメソッドは、リターンキーをタップした時に呼ばれるメソッド。
qiita.rbfunc textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true }
- 投稿日:2020-03-22T00:22:43+09:00