20210429のSwiftに関する記事は8件です。

[SwiftUI]navigationBarItemsにUI部品を表示する方法(初学者向け)

import SwiftUI struct ContentView: View { var body: some View { // Navigationの設定 NavigationView { Text("SwiftUI始めました") .navigationBarItems(trailing: Text("ナビゲーションバーの右側に配置")) } } } ナビゲーションバーの右側にUI部品を配置する場合はtrailingを、左側の場合はleadingを指定し設定する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

for文 swift

■for文一つにしても色々ある。 ■数字 for i in 1..<5 { print(i) } // 1,2,3,4 for i in 1...5 { print(i) } // 1,2,3,4,5 for _ in 1...5 { print(a) } // a,a,a,a,a _の場合繰り返したいものを5回分返すと言う意味になる。 ■文字 let a = "banana" for b in a { print(b) } // b,a,n,a,n,a 一と文字づつ順番に返される ■ 配列 let fruits = ["Apple","banana","Orange"] for a in fruits { print(a) } // Apple,banana,Orange 順番に返す let contacts = ["Apple": 200, "Banana": 300, "Orange": 400] for person in contacts { print(person.key) } // Apple,Banana,Orange for person in contacts { print(person.value) } // 200,300,400
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IDFA対応 IDFAの許可リクエストの説明文を日本語に対応する

完成形 AlertのタイトルはApple側が表示するもので、真ん中に表示されている説明文を日本語にも対応しました。 info.plistへ説明文の追加 Privacy - Tracking Usage Description をKeyとしてValueにユーザにAlertを出した時に表示される文言を追加します。 このままでは、英語の説明文が表示されます。(This identifier will be used to deliver personalized ads to you.) ローカライズする言語を追加 プロジェクト設定の info タブにある Localization から日本語を追加します。 InfoPlist.strings ファイルを新規作成 ファイルの新規作成で、Strings File タイプを選択し、ファイル名を”InfoPlist.strings“として作成します。 ファイル名は必ず”InfoPlist.strings”でないといけません。大文字小文字が違っても正しく認識されないので注意。 InfoPlist.strings ファイルのローカライズを設定 作成した”InfoPlist.strings“ファイルを選択し、右サイドにある情報パネルの Localize… ボタンで日本語を選択します。(先の手順で日本語を追加したのでローカライズ言語が選択できるようになっています) InfoPlist.strings にローカライズ用のテキスト設定を記載 “InfoPlist.strings“に、ローカライズ用のテキストを追加します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ios admobで、GADInvalidInitializationExceptionが消えなくて泣きそうだった話

admobに登録してあるアプリのメンテナンスしようとして、 を参考に作業してみようかなと思って pod update の後、buildしてみると、 Terminating app due to uncaught exception 'GADInvalidInitializationException', reason: 'The Google Mobile Ads SDK was initialized without AppMeasurement. Google AdMob publishers, follow instructions here: https://googlemobileadssdk.page.link/admob-ios-update-plist to include the AppMeasurement framework and set the -ObjC linker flag という例外でアプリがクラッシュしまくる。 言われた通りにInfo.plistを編集してるだけなのだが。。。 ググりまくったら、以下の解決法を発見。 これで良いのかわからないけど、とりあえず動くようになった。 解決法 Info.plistに <key>GADIsAdManagerApp</key> <true/> を追加。 参考リンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swiftでカウントアップのアニメーションを実装する。

目的とするアニメーション 別のクラスを作成するため、比較的キレイにコードが書けると思います。 CountAnimateLabel.swift import UIKit final class CountAnimateLabel: UILabel{ var startTime: CFTimeInterval! var fromValue: Int! var toValue: Int! var duration: TimeInterval! func animate(from fromValue: Int, to toValue: Int, duration: TimeInterval) { text = "\(fromValue)" // 開始時間を保存 self.startTime = CACurrentMediaTime() self.fromValue = fromValue self.toValue = toValue self.duration = duration // CADisplayLinkの生成 let link = CADisplayLink(target: self, selector: #selector(updateValue)) link.add(to: .current, forMode: .default) } // 描画タイミング毎に呼ばれるメソッド @objc func updateValue(link: CADisplayLink) { // 開始からの進捗 0.0 〜 1.0くらい let dt = (link.timestamp - self.startTime) / duration // 終了時に最後の値を入れてCADisplayLinkを破棄 if dt >= 1.0 { text = "\(toValue!)" link.invalidate() return } // 最初の値に進捗に応じた値を足して現在の値を計算 let current = Int(Double(toValue - fromValue) * dt) + fromValue text = "\(current)" } } このクラスをインスタンス化して、任意のタイミングでanimateメソッドを呼び出します。 CountAnimateLabel.swift import UIKit final class ShowSumViewController: UIViewController { var label: CountAnimateLabel! override func viewDidLoad() { super.viewDidLoad() setUpViews() } private func setUpViews() { label = CountAnimateLabel() let labelWidth = 200 let labelHeight = 100 label.frame = CGRect(x: (Int(view.frame.size.width) - labelWidth) / 2 , y: (Int(view.frame.size.height) - labelHeight) / 2, width: labelWidth, height: labelHeight) label.textColor = .black label.textAlignment = .center label.font = label.font.withSize(100) view.addSubview(label) label.animate(from: 0, to: 100, duration: 0.7) } } fromは開始時の数字、toは終了時の数字、durationは時間を表しているので、適宜変更してください。 参考 使わせていただきました ?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 macでUITestが実行できない

環境 2021/04/29時点 M1チップのMacBook airを使用 rosettaを利用してXcodeを利用 起きた問題 swiftのプロジェクトを作成すると一緒に作成されるUITestを実行したところ、下記のエラーで実行に失敗する Details Could not attach to pid : “29327” Domain: IDEDebugSessionErrorDomain Code: 3 Failure Reason: attach failed ((os/kern) invalid argument) User Info: { DVTRadarComponentKey = 855031; RawLLDBErrorMessage = "attach failed ((os/kern) invalid argument)"; } -- System Information macOS Version 11.1 (Build 20C69) Xcode 12.4 (17801) (Build 12D4e) Timestamp: 2021-04-29T15:15:21+09:00 解決方法 Xcodeをrosettaを利用して利用しない(原因は不明)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReadableContentGuideを利用してUICollectionViewのコンテンツを見やすくする

環境 Swift 5.4 Xcode 12.5 概要 ReadableContentGuide(以下RCG) は、端末によって コンテンツの読みやすい幅を実現するために役立つ UILayoutGuide です。 Apple Developer ドキュメント - readableContentGuide デザインによるコンテンツのマージン指定が特になければ、 UIDevice.current.userInterfaceIdiom を判定して iPhoneとiPadそれぞれに制約を設けることをせず、 RCGを利用して良い感じにマージンを設定することができます。 UITableViewやUIScrollViewで利用している例はちょこちょこ見かける気がするので、 今回はUICollectionViewの画面でRCGを利用した例を紹介します。 ※注意 Storyboradは利用せず、コードでの実装です。 実装 UICollectionViewに対してRCGを適用する UICollectionViewの制約をかける際に、 親Viewの SafeAreaLayoutGuide ではなくRCGを基準とします。 そのためitemSizeを算出する時は、RCGの layoutFrame.width を利用します。 コンテンツ上下左右のマージンはRCGに任せた実装になります。 import UIKit final class ReadableCollectionViewController: UIViewController { private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) private let itemSpacing: CGFloat = 10 override func loadView() { super.loadView() view.backgroundColor = .systemBackground } override func viewDidLoad() { super.viewDidLoad() collectionView.backgroundColor = .secondarySystemBackground collectionView.delegate = self collectionView.dataSource = self collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") collectionView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor), collectionView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor), collectionView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor) ]) } } extension ReadableCollectionViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 30 // 任意の数 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.backgroundColor = .gray return cell } } extension ReadableCollectionViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let cellSideLength: CGFloat = (view.readableContentGuide.layoutFrame.width - itemSpacing * 2) / 3 return .init(width: cellSideLength, height: cellSideLength) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { .zero } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { itemSpacing } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { itemSpacing } } 画面キャプチャ インジケータがコンテンツと被ってしまっているのでinsetの設定はあった方が良さそうです。 また、CollectionViewと親Viewの背景色が異なる場合は注意が必要です。 iPhone8 iPadPro(12.9-inch) UICollectionViewのセクションに対してRCGを適用する 先の実装とは異なり、UICollectionViewの制約は、 親Viewの SafeAreaLayoutGuide を基準とします。 その代わりにSectionのInsetを設定する箇所で 計算処理を追加しています。 ※ 先の実装と変更がない箇所は処理を省略しています import UIKit final class ReadableCollectionViewController: UIViewController { // 先の実装と同様 override func viewDidLoad() { super.viewDidLoad() // 先の実装と同様 NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor) ]) } } extension ReadableCollectionViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // 先の実装と同様 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // 先の実装と同様 } } extension ReadableCollectionViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // 先の実装と同様 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let topOrBottomInset: CGFloat = (view.frame.height - view.readableContentGuide.layoutFrame.height) / 2 let leftOrRightInset: CGFloat = (view.frame.width - view.readableContentGuide.layoutFrame.width) / 2 return .init(top: topOrBottomInset, left: leftOrRightInset, bottom: topOrBottomInset, right: leftOrRightInset) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { // 先の実装と同様 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { // 先の実装と同様 } } 画面キャプチャ CollectionViewが画面全体に広がっているのでインジケータは見やすくなりました。 Sectionが複数になった時にtopとbottomのマージン設定を改善しないといけなさそうです。 iPhone8 iPadPro(12.9-inch) 後記 今回実装した画面は動的にitemのサイズが切り替わらないので UICollectionViewFlowLayoutのインスタンスをCollectionViewに渡して実現しようとしましたが、 viewWillAppear(_:) まではRCGのlayoutFrameの正確な値を 取得できなかったため、Delegateを利用しています。 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) print("viewWillAppear:\(view.readableContentGuide.layoutFrame)") } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() print("viewWillLayoutSubviews:\(view.readableContentGuide.layoutFrame)") } // iPhone8の場合 // viewWillAppear:(0.0, 0.0, 375.0, 667.0) // viewWillLayoutSubviews:(16.0, 20.0, 343.0, 647.0)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIでPHPhotoPickerを使用する

この記事は何? PHPhotoPickerをSwiftUIで使用するアプリを作成します。 実行環境 Swift 5.3 Xcode 12.5 macOS 11.3 Photosフレームワーク 「デバイスに保存された写真」を取得するための、プライバシーに配慮した全く新しいフレームワークです。 ハンズオン 順を追って、アプリを作成していきます。 基本の画面 基本画面には「写真を表示するための枠」と「写真ピッカーを開くためのボタン」を配置します。 まずは、「写真を表示する枠」のビューを作成しておきます。 PhotoFrame.swift import SwiftUI struct PhotoFrame: View { @Binding var image: Image? var body: some View { if let image = image { image .resizable() .aspectRatio(contentMode: .fit) } else { Image(systemName: "photo") .font(.title) .foregroundColor(.gray) } } } 次に、「写真ピッカーを開くためのボタン」を作成します。 ShowPickerButton.swift import SwiftUI struct ShowPickerButton: View { @Binding var isShowLibrary: Bool var body: some View { Button(action: { isShowLibrary = true }, label: { Text("Choose Photo") .font(.title2) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(10.0) }) } } こららのビューを基本画面に配置します。 ContentView.swift import SwiftUI struct ContentView: View { @State private var isShowPhotoLibrary = false @State var image: Image? @EnvironmentObject var classifier: ImageClassifier var body: some View { ZStack { PhotoFrame(image: $image) VStack { Spacer() Text(classifier.classificationLabel) ShowPickerButton(isShowLibrary: $isShowPhotoLibrary) } } .sheet(isPresented: $isShowPhotoLibrary, content: { PhotoPicker(isPresented: $isShowPhotoLibrary, seledtedImage: $image) }) } } 写真ピッカーのビューを作成する UIKitのビューコントローラをSwiftUIで使用するは、ViewControllerRepresentableプロトコルに適合した構造体でラップします。 PhotoPicker.swift import SwiftUI import PhotosUI struct PhotoPicker: UIViewControllerRepresentable { @Binding var isPresented: Bool @Binding var seledtedImage: Image? func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration() configuration.filter = .images let picker = PHPickerViewController(configuration: configuration) picker.delegate = context.coordinator return picker } func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { // do nothing. } } PhotoPicker型を定義する デリゲートメソッドを介してPhotoPickerViewControllerとやり取りするには、仲介役となるCoordinatorクラスを定義する必要があります。 PhotoPicker.swift コーディネーターを定義する このクラスは、PhotoPicker構造体のネスト型として定義します。 Coordinatorクラス import SwiftUI import PhotosUI struct PhotoPicker: UIViewControllerRepresentable { @Binding var isPresented: Bool @Binding var seledtedImage: Image? func makeUIViewController(context: Context) -> PHPickerViewController {...} func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { // do nothing. } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: PHPickerViewControllerDelegate { private let parent: PhotoPicker init(_ parent: PhotoPicker) { self.parent = parent } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { parent.isPresented = false let itemProvider = results.first?.itemProvider // itemProvider is NSItemProvider? Type. if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) { itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in if let uiImage = image as? UIImage { self.parent.seledtedImage = Image(uiImage: uiImage) } } } } } } 最後に WWDC2021で、PHPickerViewがSwiftUIネイティブになったらいいなー。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む