20210416のSwiftに関する記事は10件です。

【Swift】NotificationCenterのuserInfoで値の受け渡しをする

はじめに NotificationCenterで値の受け渡しをする方法を解説します。 実装 import UIKit final class ViewController: UIViewController { @IBOutlet private weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(someFunc), name: .someName, object: nil) } @objc private func someFunc(notification: Notification) { if let num = notification.userInfo?["someKey"] as? Int { label.text = String(num) } } @IBAction private func buttonDidTapped(_ sender: Any) { NotificationCenter.default.post(name: .someName, object: nil, userInfo: ["someKey": 10]) } } extension Notification.Name { static let someName = Notification.Name("someName") } 解説 まず、Notificationの名前を定義しておきます。 extension Notification.Name { static let someName = Notification.Name("someName") } 次に、NotificationCenterに先ほど定義したNotificationのNameを持つObserverを追加します。 ここで登録したセレクター関数は任意のタイミングでpostした時に呼ばれる関数になります。 override func viewDidLoad() { NotificationCenter.default.addObserver(self, selector: #selector(someFunc), name: .someName, object: nil) } 任意のタイミングでpostして先ほど登録しておいたObserverに処理をするよう通知を発行します。この時、引数のuserInfoに辞書で渡したい値を設定します。 @IBAction private func buttonDidTapped(_ sender: Any) { NotificationCenter.default.post(name: .someName, object: nil, userInfo: ["someKey": 10]) } セレクター関数を定義します。ここで先ほどpostした時に設定した値をNotificationを通して受け取ります。 @objc private func someFunc(notification: Notification) { if let num = notification.userInfo?["someKey"] as? Int { label.text = String(num) } } おわりに おわりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift5]CompositonalLayoutのinset管理にlayoutMarginsを使うと楽になる

TL;DR collectionViewにlayoutMarginを設定する。 compositionalLayoutのsectionでcontentInsetsReferenceをlayoutMarginsにする。 sectionのinsetをスッキリ統一。 CompositonalLayoutのsection構築におけるinsetの管理問題。 CompositonalLayoutとはiOS13から登場したUICollectionViewのレイアウト構築用フレームワーク(?)です。 以下のようにsection、group、itemによってレイアウトを構築できます。 func createLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in let section: NSCollectionLayoutSection let sectionType = SectionLayoutKind.allCases[sectionNumber] switch sectionType { case .name: let item = NSCollectionLayoutItem( layoutSize: .init( widthDimension: .fractionalWidth(1), heightDimension: .estimated(44) ) ) let group = NSCollectionLayoutGroup.horizontal( layoutSize: .init( widthDimension: .fractionalWidth(1), heightDimension: item.layoutSize.heightDimension ), subitems: [item] ) section = .init(group: group) section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16) // insetを調整 case .desc: ... } return section } return layout } しかしcaseが増えてくるとsectionごとに以下のようなinsetを指定するのが面倒になってきます。 section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16) sectionを一時変数にしてreturnする前に設定すれば上記のように何度もinsetを指定することは回避できます。 func createLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in let section: NSCollectionLayoutSection let sectionType = SectionLayoutKind.allCases[sectionNumber] switch sectionType { case .name: ... section = .init(group: group) case .desc: ... section = .init(group: group) } section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16) return section } return layout } しかし、上記の方法だとsectionごとにちょっと上下の幅を増やしたりができません。 一応個別にsectionにinsetを指定し、途中でreturnさせれば特定のsectionだけ別のinsetにできます。 func createLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in let section: NSCollectionLayoutSection let sectionType = SectionLayoutKind.allCases[sectionNumber] switch sectionType { case .name: ... section = .init(group: group) case .desc: ... section = .init(group: group) section.contentInsets = .init(top: 16, leading: 16, bottom: 16, trailing: 16) return section // 個別にreturn } section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16) return section } return layout } けど他のsectionのinsetと横幅がずれないようにしたりするために結局defaltのinsetの幅を確認しに行ったりするのが手間です。 理想はデフォルトのinsetはそのままにsectionごとに幅のプラスやマイナスを指定できたらいいのになと思いました。 LayoutMargingsを使えばできた NSCollectionLayoutSectionにはcontentInsetsReferenceというプロパティが生えています。 これに.layoutMargingsを指定すると、CollectionViewのlayoutMarginをデフォルトのinsetとして使うようになります。 collectionView.layoutMargins = .init(top: 8, left: 16, bottom: 8, right: 16) section.contentInsetsReference = .layoutMargins するとsectionで指定したinsetはこのlayoutMarginからと足された値になります。 reference指定なし referenceにlayoutMargingsを指定 例えば、layoutMargingsが以下の場合 collectionView.layoutMargins = .init(top: 8, left: 16, bottom: 8, right: 16) 以下のnameというsectionのtopのinsetは marginの8とsectionProvider内で指定した8の合計で16になります。 func createLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in let section: NSCollectionLayoutSection let sectionType = SectionLayoutKind.allCases[sectionNumber] switch sectionType { case .name: ... section = .init(group: group) section.contentInset.top = 8.0 } section.contentInsetsReference = .layoutMargins // (top: 8, left: 16, bottom: 8, right: 16)がデフォルトのinsetになる。 return section  } return layout } これで「デフォルトのinsetは保ちつつ、sectionごとにinsetを調整する」ということが楽にできるようになりました。 結論 contentInsetsReferenceに.layoutMarginsを使うとCollectionViewのlayoutMarginをデフォルトのinsetにできる sectionにcontentInsetsを指定すればデフォルトのinsetを増減させられるので個別にinsetを指定したsectionをreturnしなくてよくなる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SCNVector3の合成

単純に+はできないので extension SCNVector3 { func plus(with:SCNVector3) -> SCNVector3 { let x = self.x + with.x let y = self.y + with.y let z = self.z + with.z return SCNVector3(x, y, z) } } SCNVector4とかCGRectでもできそう。 ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLを使ったアプリを作っています。 機械学習関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftKotlinの導入

はじめに SwiftKotlinというSwiftソースをKotlinソースに変換してくれるOSSがある。 https://github.com/angelolloqui/SwiftKotlin Mac、Xcodeになれていないため、導入に時間がかかったためメモしておく。 導入 公式ページのInstallationを参考すればOK。 ビルド時のターゲットが2つある。 そのためCommandLine版は切り替えてからビルドする必要がある。 (ということが、わかるのに時間がかかった。) ↓デフォルト状態(Appをビルド) ↓選択して切り替える(CommandLineをビルド) 下記のような場所に出力される。 /Users/ユーザ名/Library/Developer/Xcode/DerivedData/SwiftKotlin-XXXXX/Build/Products (あるいはXcodeでAppをデバッグ実行状態にして、バーのアイコンから「右クリック>オプション>Finderに表示」) 実行 公式ページのUsageを参考にすればOK。 ただし、ビルドしたCommandLineを/usr/local/binに配置してもライブラリ参照エラーが出た。 結局フォルダごと好きな場所に配置して、$PATHに通して使うという対応をとった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

App Development with Swiftに合格してきた

はじめに iOSエンジニアになり早9ヶ月。 弊社も資格取得に力を入れている中、せっかくならiOS関連の資格を何か取得できないかと思い、"App Development with Swift"を受験してみることに。 取得にあたり、Qiitaや他のサイトでも合格体験記があまりなかったので、せっかくなのでここに残すことにします。 これから取得を目指す人、何か資格を探している人などは是非読んでいってもらえたらと思います。 ちなみに受験時の筆者スペック - iOS開発歴:9ヶ月 - センター試験・英語:150点(くらいだった気がする。なお5年前) - TOEIC:450点(くらいだった気がする。なお3年前) 試験概要 資格名:App Development with Swift 科目名:App Development with Swift Level 1 試験時間:50分 試験の言語:英語(2021年4月現在、日本語対応は未定) 試験方式:CBT試験 問題数:45問 合格ライン:(おそらく)7割 要はSwift、Xcodeなど、iOSアプリ開発者として必要な知識、スキルを証明する資格です。 Level2はまだ無いのではないでしょうか。筆者が調べてみた限りでは見つからなかったですね。 詳しくはこちらのサイトをご覧ください。 App Development with Swift 公式HP 出題範囲 大きく分けてセクションは以下の3つです。 Developer Tools Xcode & Builder Interface Swift Language Fundamentals iOS API Fundamentals 上記サイトにて、学習教材が提供されているので、そちらをやれば十分かと。 ただし、過去問などは一切出回っていないので、他の試験で過去問をやりまくって合格してきた人は大変かもしれません。 逆に言うと、実際に開発経験のある方はそこまで難しくないと言えます。 Swiftの基本的文法から、Xcodeの説明、ライフサイクルやMVCなど満遍なく出題されます。 出題形式 選択問題(択一問題)・・・複数の選択肢の中から一つ選択 選択問題(複数回答)・・・選択肢の中から複数を選んで回答 記述問題・・・ソースコードの穴になっている箇所の補完、文章が正しくなるように単語の記述など ドラッグ&ドロップ・・・並び替え、選択肢を対応するものにドラック&ドロップ、問題に対応する箇所をクリック Apple推奨の教材にも問題は載っていますが、ほぼほぼ参考にならないので、自分で手を動かして一つずつ理解していくことをオススメします。 勉強法 Apple推奨教材を読み込む 日頃の開発(笑) 以上です!笑 問題集がない以上、やはり手を動かさなければ理解できないです。 私の場合は、たまたま開発業務があったので、それである程度は実践経験を積めたのでよかったですが、もし開発業務がないって人はApple推奨の教材に一緒にハンズオンしていく箇所もあるので、そちらを進めることをオススメします。 しかしながら、Xcodeの画面を見て答える問題やAutoLayoutの説明、UIパーツの説明などは、やはり日頃から開発に触れていないと答えるのが厳しい気がします。なので、簡単なアプリでも良いので作ってみることをオススメします。 開発はやっていないけど資格だけ取ろうって人はかなり厳しいのではないかと。 開発をやっている人前提の資格というイメージですね。 受験結果と感想 正直に言います。1回目は落ちました。笑 推奨教材が900ページを超えるということと、その900ページが全て英語ということで、英語弱者の筆者としてはやりたくなかったです。 日頃の開発知識だけでどうにかしようと実質ノー勉で行きました。 が、結果は1000点満点中680点でした。700点が合格ラインなはずです。 敗因としては、 1. 開発でカバーしている箇所とそうでない箇所の理解の差が激しい 2. 無意識に使用しているものをいざ聞かれたときに回答できない この2点だったかなと思います。 ということで、1ヶ月後にもう一度受けました。 その際は、教材とGoogle翻訳をiPadに竝べ、開発で触れない箇所を重点的に勉強しながら、ひたすらコピペで読み進めました。 そうして迎えた再試験、なんとか830点とって合格でした。 英語は実際どうなの? 多くの方が気になる箇所って、これだと思うんですよ。 ご存知の通り、問題文、回答全て英語です。そりゃそうですよね、Apple主催の試験なんですから。笑 筆者もこれがネックで受験することを渋っていました。筆者の英語スペックは上記に挙げた通りで、極力英語に触れないように生きてきました。 しかし、受けてみての感想は、ぶっちゃけあまり関係ないです。 「以下の選択肢から選びなさい」 「上記のソースコードの空白部分に対応するものを選びなさい」 「画面で対応する箇所をクリックしなさい」 「正しい順番に並べ替えなさい」 などなど、なんとなく読まなくても問題を見れば文章は予測できます。 たまに、絶妙に単語の意味がわからなくて2択で悩んだりしますが。笑 また、45問の問題数に対して試験時間は50分あるので、1問に1分かけたとしてもお釣りがきます。ゆっくり文章を読んでもらって大丈夫です。 筆者の場合は、2回目は一周して見直しをしても30分ほどで試験は終了しました。 結局大事な知識は、英語力よりもSwiftやXcodeに関する知識です。 これがあれば多少英語で詰まって怪しい問題があったとしても、合格点数を下回ることはないでしょう。 最後に 今回二度目ではありましたが、なんとか合格できてよかったなという感じです。 普段使わない分野や、これから開発で使おうとしていた知識も身につけることができたので、開発の質や守備範囲も広がったなという印象です。 引き出しの数は多くて損することはないですね。 iOS開発をしている方は、是非知識整理やスキルチェックのためにも受験してみてはいかがでしょうか。 AppleがLevel2を出したら、そちらも受験してみようかなと思います。 それでは。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SwiftUI】最低限のコードでカメラのプレビューを表示する

SwiftUI上で必要最低限のコードでカメラのプレビューを表示します。 フロントカメラのみ デバイスの向きはポートレートのみ カメラのアクセス許可のチェックなし 環境 Swift: 5.3.2 Xcode: 12.4 (12D4e) macOS: Big Sur 11.1 (20C69) コード ContentView.swift import SwiftUI import AVFoundation struct ContentView: View { var body: some View { CameraView() .edgesIgnoringSafeArea(.all) } } struct CameraView: UIViewRepresentable { func makeUIView(context: Context) -> UIView { BaseCameraView() } func updateUIView(_ uiView: UIViewType, context: Context) {} } class BaseCameraView: UIView { override func layoutSubviews() { super.layoutSubviews() _ = initCaptureSession (layer.sublayers?.first as? AVCaptureVideoPreviewLayer)?.frame = frame } lazy var initCaptureSession: Void = { guard let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .unspecified) .devices.first(where: { $0.position == .front }), let input = try? AVCaptureDeviceInput(device: device) else { return } let session = AVCaptureSession() session.addInput(input) session.startRunning() layer.insertSublayer(AVCaptureVideoPreviewLayer(session: session), at: 0) }() } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } リポジトリ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】Swift UIを勉強する その⑪ ーーー Lazy Grid Layout(= StackView+CollectionView)

はじめに 前回はCardに関するアニメーションを完璧にし、今回はCardを縦並びのみならず、もっと自由に位置を決めるようにします。 完成品↓    目次 LazyVGrid LazyHGrid まとめ 参考文献 LazyVGrid ・Lazy Grid LayoutはWWDC2020に公開されました。CollectionViewでは一日かかる作業は、Lazy Grid Layoutでは昼ごはんくらいの時間でできてしまいます。 html+css+jsからVue+Vuetify+Tailwindに切り替えたように、かなり作業効率がアップ ・例えばcolumns[]配列にGridItem()を3つつめば、Cardが3列に並びます。 CoursesView.swift LazyVGrid(columns: [GridItem(), GridItem(), GridItem()], spacing: 16) { ForEach(courses) { item in CourseItem(course: item) .matchedGeometryEffect( id: item.id, in: namespace, isSource: !show ) .frame(height: 250) .onTapGesture { withAnimation(.spring()) { show.toggle() selectedItem = item isDisabled = true } } .disabled(isDisabled) } } .padding(16) .frame(maxWidth: .infinity)    ・GridItem()を一個一個つめるのはスマートじゃないので、  GridItem(.adaptive(minimum: )で最少幅を決めることもできます。  また、iPadのような画面が大きい端末の場合は、数字ではなく  UIScreen.main.bounds.widthを使えば全ての端末は統一できます。  もう1つのやり方としてはArray()を使って、countの数を決めれば列が決められます。 CoursesView.swift LazyVGrid( columns: [ GridItem( .adaptive(minimum: 160), spacing: 16 ) ], spacing: 16 ) --------------------------------------------------- LazyVGrid( columns: [ GridItem( .adaptive(minimum: UIScreen.main.bounds.width / 3), spacing: 16 ) ], spacing: 16 ) ---------------------------------------------------- LazyVGrid( columns: Array( repeating: .init(.flexible(), spacing: 16), count: 2 ), spacing: 16 ), spacing: 16    LazyHGrid ・verticalと同じくhorizontalも簡単にできます。やり方もverticalと全く同じです。 CoursesView.swift ScrollView(.horizontal) { LazyHGrid( rows: [ GridItem( .adaptive(minimum: 160), spacing: 16 ) ], spacing: 16 ) }   まとめ ・LazyGridはおそらくフロントのGrid systemを参考にしていると思われます。  興味がある方は→https://vuetifyjs.com/ja/components/grids/ ソースコードGithub 参考文献 Design Code --- SwiftUI for iOS 14
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

StoryboardとXibと繋げる方法

Xibを作成 Viewも一緒に作成してください。 Class名を統一させてください。 VeiwController側でxibを呼び出します。 そしてViewControllerでxibを追加します let view: UIView = UINib(nibName: "buttonView", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView self.view1.addSubview(view)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftでのクラス

WHY 最近日常で実際に使ってみたいアプリを思いつき、それを実現させるため、Swiftを勉強し始めました。今後はswiftについてのアウトプットが増えていくと思います、今回は基本のSwiftでのクラスについてアウトプットしていきます、 クラスとは クラスとは簡単にいうと設計図です、その設計図を元にインスタンス、つまり「もの」を作ることができます。 考え事態はRubyなどと同じです。 クラスの継承 Swiftで重要になってくるのがこのクラスの継承、、 継承は以下のように書きます、 //パターン1 @IBOutlet weak var imageView: UIImageView! //パターン2 var timer = Timer() パターン1ではvarで宣言し、「imageView」というインスタンスを「UIImageView」というクラスで継承しています、 言い換えれば画面上の文字や画像などの要素を変数化(初期化)したと考えればいいと思います、 「UIImageView」以外にも「UILabel」(テキストの要素)、「UIButton」(ボタンの要素)などのクラスがあらかじめ用意されています。 パターン2では単純に「=」でクラスを指定しているような形になります。 これでも「timer」というインスタンスは「Timerクラス」を継承しているという形です。 二つの違いは画面上に文字や画像などの要素があるかないかです。 そしてそれらのクラスは「UIKit」というものの中に入っています。 import viewcontrollerファイルをみてみるとはじめに import UIKit という記述があります。 これはあらかじめSwiftが用意されているクラスや機能を全てこのファイルで使えるようにするという宣言のようなものです。 ほかにもpodというライブラリなどを導入することでこのimportは追加記述することができます、 つまりこのimportを増やすことで、使えるクラスやメソッドや機能が増えていくということです、 簡単にアウトプットしていきましたがこんな感じで少しずつやっていこうと思います
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xibのサイズ変更

SizeをFreeformにします。 すると上記の画面でもXib上でも変更できるようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む