20210724のSwiftに関する記事は7件です。

FSCalendarの日付をタップしたら、TextFieldを持ったAlertを表示する

こんな感じです コードと簡単説明 まずは、表示するAlertを作成します。 アラートにTextFieldを表示するには、alert.addTextField(configurationHandler:((UITextField) -> Void)?)を使用します。 Alert func showTextFieldAlert(targetView:UIViewController){ var productNameTextField:UITextField? //Alertに表示したTextField.Textを入れて使います var janCodeTextField:UITextField? var deadlineDayTextField:UITextField? let alert = UIAlertController(title: "データを追加", message:"データを追加する場合は、値を入力して下さい", preferredStyle: .alert) alert.addTextField { (alertProductNameTextField:UITextField) in alertProductNameTextField.placeholder = "プレスホルダー" productNameTextField = alertProductNameTextField) // } alert.addTextField { (alertJANCodeTextField:UITextField) in alertJANCodeTextField.placeholder = "プレスホルダー" janCodeTextField = alertJANCodeTextField } alert.addTextField { (alertDeadlineDayTextField:UITextField) in alertDeadlineDayTextField.placeholder = "プレスホルダー" deadlineDayTextField = alertDeadlineDayTextField } alert.addAction(UIAlertAction(title: "キャンセル", style: .default, handler: nil)) alert.addAction(UIAlertAction(title: "追加", style: .default, handler: { _ in //追加が押された時の処理 })) targetView.present(alert, animated: true, completion: nil) } FSCalendarで日付のタップを検知するには、func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {}を使用します。 FSCalendar func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { //カレンダーの日付がタップされた時に働く処理 } 実際にアプリで書いたコード Alert Alert extension Alert{ func showTextFieldAlert(targetView:UIViewController){ let realmCRUDModel = RealmCRUDModel() var productNameTextField:UITextField? var janCodeTextField:UITextField? var deadlineDayTextField:UITextField? let alert = UIAlertController(title: "データを追加", message:"データを追加する場合は、値を入力して下さい", preferredStyle: .alert) alert.addTextField { (alertProductNameTextField:UITextField) in alertProductNameTextField.placeholder = "商品名" productNameTextField = alertProductNameTextField } alert.addTextField { (alertJANCodeTextField:UITextField) in alertJANCodeTextField.placeholder = "JANコード" janCodeTextField = alertJANCodeTextField } alert.addTextField { (alertDeadlineDayTextField:UITextField) in alertDeadlineDayTextField.placeholder = "期限 例:2021年01月01日" deadlineDayTextField = alertDeadlineDayTextField } alert.addAction(UIAlertAction(title: "キャンセル", style: .default, handler: nil)) alert.addAction(UIAlertAction(title: "追加", style: .default, handler: { _ in realmCRUDModel.createRealm(createProductName: (productNameTextField?.text)!, createJANCode: (janCodeTextField?.text)!, createDeadlineDay: (deadlineDayTextField?.text)!) })) targetView.present(alert, animated: true, completion: nil) } } FSCalendar ViewController func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { alert.showTextFieldAlert(targetView: self) } 終わり 初めて、TextField入りのAlertを書いた時に Alert内のTextField.textをどうすれば使えるのか分からなかったのが懐かしい 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIでLottieを使ってリッチなアニメーションを簡単に実現してみる

以下の記事を以前書いたが、最近SwiftUIを使ってLottieを利用したのでその方法について書いていく。 Lottieに関する説明はいろいろな記事でもされているのでここでは触れないで、SwiftUI上での利用方法について見ていく。 インストール インストール方法はSwiftUIを使うからと言って特には変わらない。GitHubの説明に従う。CocoaPodsを利用する場合は、Podfileに以下を追加してpod installをターミナルで呼ぶだけ。コード内でimport LottieとするとLottieが利用可能になる。 # Podfile pod 'lottie-ios' コード 以下のようなファイルを作る。 // LottieView.swift import SwiftUI import Lottie struct LottieView: UIViewRepresentable { private let tag = 100 // 必要があれば変えてください var name: String var loopMode: LottieLoopMode = .playOnce var animationView = AnimationView() @Binding var playing: Bool func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView { let view = UIView(frame: .zero) animationView.tag = tag animationView.animation = Animation.named(name) animationView.contentMode = .scaleAspectFit animationView.loopMode = loopMode animationView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(animationView) NSLayoutConstraint.activate([ animationView.heightAnchor.constraint(equalTo: view.heightAnchor), animationView.widthAnchor.constraint(equalTo: view.widthAnchor) ]) return view } func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) { if playing { (uiView.viewWithTag(tag) as! AnimationView).play() } else { (uiView.viewWithTag(tag) as! AnimationView).stop() } } } 使い方は以下のような感じ。ローディングビューのようなコンポーネントだと表示中は常に再生をしていたいので、loopModeには.loopを、playingには.constant(true)を渡す。nameにはアニメーションのJSONファイルの名前を渡す。 struct LoadingView: View { var body: some View { LottieView(name: "loading", loopMode: .loop, playing: .constant(true)).frame(width: 250, height: 250) } } struct SomeView: View { @State var loading: Bool var body: some View { ZStack { // メインのビュー Text("Contents") // loadingがtrueの時のみLoadingViewを表示 if loading { LoadingView() } } } } 一方で、ユーザのインタラクションに応じて、再生状態を変えたいようなケースは以下のようになる。 struct PlayerView: View { @Binding var playing: Bool var body: some View { LottieView(name: "player", loopMode: .loop, playing: playing).frame(width: 250, height: 250) } } struct SomeView: View { @State var playing: Bool var buttonImage: String { playing ? "ic_playing" : "ic_play" } var body: some View { ZStack { PlayerView(playing: $playing) Button(action: { playing.toggle() }) { Image(buttonImage) } } } } まとめ Lottieがあればアニメーション周りはエンジニアとしては楽できるので非常に便利なツール。SwiftUIでも導入できてとても良い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift エラー】Method '' with Objective-C selector 'delete:' conflicts with method 'delete' from superclass 'UIResponder' with the same Objective-C selector

storybordから引っ張って、deleteという関数を作成しようとしたときに発生したエラー。 以下のように表示された。 Method 'delete' with Objective-C selector 'delete:' conflicts with method 'delete' from superclass 'UIResponder' with the same Objective-C selector 原因 よくわからないが、調べているとコンパイルエラーが発生しているらしい。 Swiftではオーバーロードが可能だが、Objective-cではオーバーロードができないため発生する。 Swiftでは引数または返り値が違う場合は同じ名前(識別子)で複数の関数を定義することができ、これを「オーバーフロー」という。 つまり、引数が文字列(String)や数字(Int)で違うなら、関数の名前は同じでもいいというもの。1つでもどこか違うなら、名前は同じでいいよってことですね。(ややこしいけど) 解決策 ・名前を変える 私の場合、メモの削除機能だったため、deleteだった関数名をdeleteMemoとかに変更したらエラーは出なくなった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】クロージャについて整理してみた

はじめに クロージャについての理解がまだまだ浅く、なんとなくの理解となっているため、 私の頭の中を整理する意味でまとめていきます。 クロージャについて そもそもクロージャとはなにか? についてSwift実践入門より以下を引用させていただきます。 クロージャは、再利用可能なひとまとまりの処理です。 クロージャにはクロージャ式という定義方法があります。 クロージャ式は名前が不要であったり、型推論によって型の記述が省略可能であったりと、関数よりも手軽に定義できます。 基本構文 クロージャは{ }を使用して、以下のフォーマットで記述します。 { (引数名1:型,引数名2:型....) -> 戻り値の型 in //実行時の処理 return 返却値 } 上記に従い実際に記述すると以下のようになります。 let equal = { (val1:Int,val2:Int) -> Bool in //実行時の処理 return val1 == val2 } (val1:Int,val2:Int)でInt型の値を2つ受け取り、Bool型の値を返します。 上記で実際に返却される値はval1とval2を比較したBool型の結果です。 これを変数equalに代入しています。 クロージャの呼び出し クロージャを呼び出す際は、クロージャが代入されている変数名の後ろに( )をつけて、( )の中に受け渡す値を記述します。 let equal = { (val1:Int,val2:Int) -> Bool in //実行時の処理 return val1 == val2 } //呼び出し equal(10,20)//false equal(20,20)//true 糖衣構文(シンタックスシュガー) クロージャを呼び出す際の値や変数宣言によって、型を自動的に推論できる場合は 、クロージャの引数の型を省略出来ます。 また、処理が単文の場合はreturnも省略可能です。 //変数宣言 let equal :(Int,Int) -> Bool //クロージャを代入 equal = { (val1,val2) in //実行時の処理 val1 == val2 } //呼び出し equal(10,20)//false equal(20,20)//true 引数が無く、戻り値も無いクロージャは以下のように ( ) -> Void in と記述出来ます。 let closure = { () -> Void in print("Hello") } //呼び出し closure()//Hello さらに、( ) -> Void in も省略出来ます。 let closure = { print("Hello") } //呼び出し closure()//Hello クロージャのスコープ //変数宣言 var closure :() -> Void do{ var count = 0 closure = { count = count + 1 print(count) } } //呼び出し closure()//1 closure()//2 //print(count) //doのスコープ外からはcountにアクセスできないためコンパイルエラー クロージャのスコープについて見ていきましょう。 do文の{}の中で宣言された変数countは、do文の{}の中のみが影響範囲となっており、本来、do文の外からのアクセスは出来ません。 しかし、クロージャは自身が定義されたスコープの変数への参照を保持しているため、do文の外からクロージャを通してcountの値を変更することが出来ます。この機能をキャプチャといいます。 トレイリングクロージャ トレイリングクロージャとは関数の引数の最後にクロージャを渡した場合にクロージャを( )の外側に記述することができる方法です。 これにより、可読性が向上します。 func greeting(name:String,handler:(String) -> Void){ handler(name) } //トレイリングクロージャあり greeting(name: "ポチ") { val in print("\(val)さんこんにちは") } //トレイリングクロージャなし greeting(name:"ポチ",handler:{ val in print("\(val)さんこんにちは") }) //実行結果 //ポチさんこんにちは //ポチさんこんにちは 参考 【Swift】クロージャ(基本編) Swift実践入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swft】デバッグ方法

ブレークポイント プログラム実行中の変数の中身を確認したいときには ブレークポイントを貼ることで、一旦実行をそこで止めることが出来ます。 ブレークポイントの設置 Xcode上の行番号をクリックすることで、ブレークポイントを貼ることができ、外すときは、ブレークポイントをドラッグすることで外すことが出来ます。 また、ショートカットキーも用意されており、ブレークポイントを貼りたい行にカーソルを移動し、 コマンド + オプション + ¥ を同時に入力することで設定出来ます。 条件設定 ある条件のときだけ実行を止めることもできます。 ブレークポイントを設定したい行番号をダブルクリックします。 Conditionという項目が条件になるので、処理を止めたい条件を入力することで、 この条件のときだけブレークポイントに処理が止まるようになります。 po コマンド 変数の中身を確認したいときはブレークポイントで実行を止めた後、Xcode下部のエリアで 「po 変数名」を入力することで変数の値が出力されます。 expression コマンド expressionコマンドを使用することで、変数の値を書き換えることもできます。「expression 変数名 = 値 」を入力することで変数の値を変更出来ます。 下記サンプルではvalueを1000に書き換えた後にpoコマンドで値を確認しています。 参考 Swift プログラムのデバッグテクニック
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CollectionViewまとめ(Swift)

UICollectionViewとは 複数のデータ項目をカスタマイズして、綺麗にレイアウトできるもの 使い方 ① UICollectionViewDelegate, UICollectionViewDataSourceのプロトコルを持ってくる delgate.swift class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { ...} ② viewdidiLoadの中で自分がつかう!ってことをしっかり宣言しておく delegate2.swift //delegateの宣言 myCollectionView.delegate = self //dataSourceの宣言 myCollectionView.dataSource = self ③ あとはお好きにカスタマイズ!! layout.swift //CollectionViewのレイアウトを初期化してあげる let layout = UICollectionViewFlowLayout() // Cell一つ一つの大きさをCGFloat型で指定 layout.itemSize = CGSizeMake(30,30) // Cellのマージンを上下左右で設定 layout.sectionInset = UIEdgeInsetsMake(10, 10, 20, 10) // セクション毎のヘッダーサイズをCGFloat型で指定 layout.headerReferenceSize = CGSizeMake(50,15) // CollectionViewに上記で作ってきたデータを引数に入れてつくる myCollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout) // Cellに使われるクラスを登録(Id、今回はMyCell) myCollectionView.registerClass(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "MyCell") ④関数で実際に描画 kansu.swift //Cellが選択された際に呼び出される関数 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { } //Cellの総数を返す func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 100 } //Cellに値を設定する func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { //さっき登録したMycellIDを型キャストして定数cellに入れてあげる let cell : UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as! UICollectionViewCell //背景色の変更 cell.backgroundColor = UIColor.redColor() //cellを返す。 return cell } まとめ 基本TableViewと使い方は変わらない 。両方の使用方法さえ理解できていれば幅広いデータのレイアウトの仕方ができそう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LivePhotosをmovとjpegから作る

LivePhotos を mov と jpeg から作る mov と jpeg を LivePhotos として保存できます 動画ファイルの時間や、mov と jepg にそれぞれメタデータの設定が必要です AVAssetWriter と CoreImage を使って、それぞれのメタデータを設定する事が可能です サンプルアプリ リポジトリはこちらです LivePhotos として保存できる条件 mov と jpeg に共通の content identifier を設定する必要があります mov を作成する AVAssetWriter の設定 ファイルのメタデータに content identifier を設定します assetWriter.metadata に設定します let assetWriter = try AVAssetWriter(outputURL: url, fileType: fileType) let item = AVMutableMetadataItem() item.key = AVMetadataKey.quickTimeMetadataKeyContentIdentifier as NSString item.keySpace = AVMetadataKeySpace.quickTimeMetadata item.value = contentIdentifier as NSString item.dataType = kCMMetadataBaseDataType_UTF8 as String assetWriter.metadata = [item] AVMetadataKey.quickTimeMetadataKeyContentIdentifier は "com.apple.quicktime.content.identifier" のようです メタデータトラックを作成して追加します let spec : NSDictionary = [ kCMMetadataFormatDescriptionMetadataSpecificationKey_Identifier: "mdta/com.apple.quicktime.still-image-time", kCMMetadataFormatDescriptionMetadataSpecificationKey_DataType: kCMMetadataBaseDataType_SInt8 ] var desc : CMFormatDescription? = nil CMMetadataFormatDescriptionCreateWithMetadataSpecifications(allocator: kCFAllocatorDefault, metadataType: kCMMetadataFormatType_Boxed, metadataSpecifications: [spec] as CFArray, formatDescriptionOut: &desc) let input = AVAssetWriterInput(mediaType: .metadata, outputSettings: nil, sourceFormatHint: desc) let adaptor = AVAssetWriterInputMetadataAdaptor(assetWriterInput: input) assetWriter.add(adaptor.assetWriterInput) 開始時刻と終了時刻と still image time のメタデータを書き込みます 録画中に書き込む必要があるようです let item = AVMutableMetadataItem() item.key = "com.apple.quicktime.still-image-time" as NSString item.keySpace = AVMetadataKeySpace.quickTimeMetadata item.value = 0 as NSNumber item.dataType = kCMMetadataBaseDataType_SInt8 as String let timeRange = CMTimeRange(start: startTime, end: latestTime) metadataAdaptor.append(AVTimedMetadataGroup(items: [item], timeRange: timeRange)) CoreImage を使った jpeg の Data の作成 jpeg は "17" のキーに content identifier を設定する必要があるようです CIImage.settingProperties を使って、プロパティを設定します CIContext.jpegRepresentation を使って、CIImage から jpeg の Data を取得します let ci: CIImage = ~~~~~ let context = CIContext() let properties: [CFString : Any] = [kCGImagePropertyMakerAppleDictionary: ["17": contentIdentifier]] let propertiedCi = ci.settingProperties(properties) let jpegData = context.jpegRepresentation(of: propertiedCi, colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, options: [:])! Live Photos の保存 PHPhotoLibrary を使って Live Photos として保存します mov を .pairedVideo として保存します try PHPhotoLibrary.shared().performChangesAndWait { let request = PHAssetCreationRequest.forAsset() request.addResource(with: .photo, data: jpegData, options: nil) let videoOptions = PHAssetResourceCreationOptions() videoOptions.shouldMoveFile = true request.addResource(with: .pairedVideo, fileURL: videoUrl, options: videoOptions) } おわりに Live Photosはなかなかドキュメントを見つけられず苦心しました PHPhotoLibrary のエラーも情報が落ちないので、AVCapturePhotoOutput の出力と見比べたり差し替えたりすることで、コンストラクタのオプションだと jpeg の設定で出来ていないことに気付いたり、など大変でした Live Photos は iOS 固有の機能なので活用して行きたいです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む