20210612のiOSに関する記事は8件です。

iOS15でのUIKitの変更箇所をまとめた。

はじめに どうも@kaneko77です。 WWDC2021が今週ありました。 月曜日の26時からだった為、定時で上がって速攻寝て25時半に起きてWWDCに備えていました。 ※WWDCのEvent参照 今回はハードウェアの発表がなかったのは残念でしたが、割と面白内容になっていて早起きした甲斐あったな〜と思いました。 発表が終わった後にBeta版入れて色々試したのでその中でiOS15の変更点を今回は共有していきたいと思います。 特に私が興味がある内容に焦点を当てて記事を書いてます。 ※SwiftUIについては触れないです。 ※まだまだ新しい技術なので情報がネットに出回っていなく共有できない箇所がありますのでそちらご了承いただけますと幸いです。 こんな方対象 iOS15で何ができるようになったか知りたい人 iOSエンジニアの方 環境 Xcode: Version 13.0 beta (13A5154h) iOSVersion: iOS15 Swift: 5.5 参考にしたサイト https://blog.prototypr.io/new-in-ios-15-for-product-designers-design-engineers-aa504e3374f8 https://github.com/apple/swift/blob/main/CHANGELOG.md https://alejandromp.com/blog/wwdc21-notes/ https://developer.apple.com/videos/play/wwdc2021/10059/ 目次 UISheetPresentationControllerで下からピョコってするやつめちゃくちゃ楽になった! UIButtonで色々できるようになった UIMenuにサブビューが追加された シンボルが追加された! そのほか 紹介 UISheetPresentationControllerで下からピョコってするやつ楽になった! こちらは半Modalを簡単に実装できる物になります。 ライブラリだとこちらをよく使ってました。 以下がUISheetPresentationControllerを使ったmodal表示になります。 こちら参考に作ってます。 最初は半Modalで表示し、ボタンを押下すると普通のModalになるように作っています。 コード まずは簡単に実装したコードを貼ります。 こちら全部コピペしてもらえれば動きます。 ※iOS15からじゃないと使えないです。 表示Type(2種類) 呼び出し元 import UIKit class ViewController: UIViewController { var mainView: MainView! override func loadView() { super.loadView() self.mainView = MainView(frame: .zero) self.view = self.mainView } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.mainView.pushButton.addTarget(self, action: #selector(tapped(_:)),for: .touchUpInside) } // MARK: タッチイベント @objc func tapped(_ sender : Any) { present(ModalViewController.init(), animated: true, completion: nil) } } class MainView: UIView { let hogeLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "テストで表示" label.font = .boldSystemFont(ofSize: 32) label.textColor = .black label.textAlignment = .center return label }() let pushButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("「ぴょこ」っと出す", for: .normal) button.setTitleColor(.white, for: .normal) button.backgroundColor = .red button.layer.cornerRadius = 25 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .systemGray setComponent() setConstraint() } private func setComponent(){ [hogeLabel, pushButton].forEach{ addSubview($0) } } private func setConstraint(){ NSLayoutConstraint.activate([ hogeLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hogeLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hogeLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), pushButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -20), pushButton.heightAnchor.constraint(equalToConstant: 50), pushButton.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), pushButton.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 呼び出し側 import UIKit class ModalViewController: UIViewController, UISheetPresentationControllerDelegate, ModalViewDelegate { var mainView: ModalView! var sheetPresentationController: UISheetPresentationController! override func loadView() { super.loadView() self.mainView = ModalView(frame: .zero) self.mainView.delegate = self self.view = self.mainView sheetPresentationController = presentationController as? UISheetPresentationController // modalのサイズ sheetPresentationController.delegate = self sheetPresentationController.selectedDetentIdentifier = .medium // 上部にグラバーを表示する sheetPresentationController.prefersGrabberVisible = true // 高さの定義 sheetPresentationController.detents = [ .medium(), .large() ] // 角丸数値 let cornerRadius = 80 // modalの角丸の数値反映 self.sheetPresentationController.setValue(cornerRadius, forKey: "preferredCornerRadius") } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // MARK: ModalViewDelegate func onTapExpansionButton() { self.sheetPresentationController.animateChanges { if self.sheetPresentationController.selectedDetentIdentifier == .medium { self.sheetPresentationController.selectedDetentIdentifier = .large } else { self.sheetPresentationController.selectedDetentIdentifier = .medium } } } } protocol ModalViewDelegate: AnyObject { func onTapExpansionButton() } class ModalView: UIView { weak var delegate: ModalViewDelegate? let hugaLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "テストでModalを表示する" label.textColor = .black label.textAlignment = .center return label }() let expansion: UIButton = { let button = UIButton() button.setTitle("拡大・縮小", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false button.setTitleColor(.white, for: .normal) button.backgroundColor = .black button.layer.cornerRadius = 16 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .yellow setComponent() setConstraint() } // MARK: タッチイベント @objc func onTapExpansionButton(_ sender : Any) { delegate?.onTapExpansionButton() } private func setComponent(){ [hugaLabel, expansion].forEach{ addSubview($0) } self.expansion.addTarget(self, action: #selector(onTapExpansionButton(_:)),for: .touchUpInside) } private func setConstraint(){ NSLayoutConstraint.activate([ hugaLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hugaLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hugaLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), expansion.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -50), expansion.heightAnchor.constraint(equalToConstant: 32), expansion.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), expansion.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 説明 重要なのはここ! コメントに書いた内容となっております。 Modalの角丸の数値は自由に変更して遊んでみてください。 // modalのサイズ sheetPresentationController.delegate = self sheetPresentationController.selectedDetentIdentifier = .medium // 上部にグラバーを表示する sheetPresentationController.prefersGrabberVisible = true // 高さの定義 sheetPresentationController.detents = [ .medium(), .large() ] // 角丸数値 let cornerRadius = 80 // modalの角丸の数値反映 self.sheetPresentationController.setValue(cornerRadius, forKey: "preferredCornerRadius") あとはボタンを押下した時の処理 sheetPresentationController.selectedDetentIdentifierで自分の設定した高さを持っています。 その為、こちらを変更すればあらびっくり変わります。 sheetPresentationController.animateChangesはアニメーション処理です func onTapExpansionButton() { self.sheetPresentationController.animateChanges { if self.sheetPresentationController.selectedDetentIdentifier == .medium { self.sheetPresentationController.selectedDetentIdentifier = .large } else { self.sheetPresentationController.selectedDetentIdentifier = .medium } } } UIButtonで色々できるようになった こちら参考 こちらも参考 UIButtonで複数行がサポートされるようになった! これまではUIButtonで複数行のテキストを表示するとなると アトリビュート を使っていたと思います。 しかし今回から楽になっちゃいました✌️ また、角丸などもcornerRadiusで定義せずとも自動でできます。 コード 複数行に対応したボタン let pushButton: UIButton = { // configの定義 var config = UIButton.Configuration.tinted() // 画像の設定 // config.image = UIImage(systemName: "person.2.circle.fill") // 画像どっち側に表示するか (何種類かあります。) // config.imagePlacement = .top config.buttonSize = .large // 角丸を自動で出す (何種類かあります。) config.cornerStyle = .capsule // 背景の設定 config.baseBackgroundColor = .systemGreen let button = UIButton(configuration: config) button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("「ぴょこ」っと出す\nひょっこりはん!!", for: .normal) button.setTitleColor(.white, for: .normal) return button }() 上記のコードを適当な画面に入れることで下記のように表示されると思います。 ちなみに画像などの設定をすると以下のように表示されます? UIButtonでインジケータの表示ができるようになった これまではライブラリなどを入れてログインボタンを押下したらAPIが走ってその際はクルクルさせるみたいなのをやっていましたが、Configでできるようになりました。 実演したのが以下になります。3秒でインジケータが終わるように書いています。 コード こちらshowsActivityIndicatorの ture or false で楽に制御できます。 画像だけクルクル回るのは気にくはない感じがある(ボソッ) // インジケーターの表示 self.mainView.pushButton.configuration?.showsActivityIndicator = true UIMenuにサブビューが追加された 多分UIMenuってIOS13からいますよね? 割と利用シーンがわからず放置していました。ActionSheet使うわってなっていましたが、 サブビュー追加は割と私の中では使ってみたいなと思いました。 ではいきましょう。 元々は2階層のものは以下のような表示でした(iOS14.5) しかしiOSから以下になります。 階層があるものは一発でわかるようになります UIが進化して使いやすくなりました。? コード 適当に作りました。 iOS15になったからと言ってこの階層の設定については..... 何もやることがありません もう一回言います。勝手にOSがやってくれるます。その為 何もやることはありません import UIKit // メニュー表示項目 enum MenuType: String { case A = "アニメ" case B = "食べ物" case C = "電話" case D = "スターウォーズ" case E = "ゴーストバスター" } class TestMenuViewController1: UIViewController { var mainView: TestMenuView1! var selectedMenuType: MenuType = .A override func loadView() { super.loadView() self.mainView = TestMenuView1(frame: .zero) self.view = self.mainView } override func viewDidLoad() { super.viewDidLoad() // UIMenuを設定 self.menuSet() } private func menuSet() { var actions = [UIMenuElement]() [MenuType.A, MenuType.B, MenuType.C].forEach{ element in let action = UIAction( title: element.rawValue, state: self.selectedMenuType == element ? .on : .off, handler: { _ in self.selectedMenuType = element // stateの更新 self.menuSet() }) actions.append(action) } var subMenus = [UIAction]() [MenuType.D, MenuType.E].forEach{ element in let action = UIAction( title: element.rawValue, state: self.selectedMenuType == element ? .on : .off, handler: { _ in self.selectedMenuType = element // stateの更新 self.menuSet() }) subMenus.append(action) } let subMenu = UIMenu(title: "映画", children: subMenus) actions.append(subMenu) // Buttonに対してUIMenuの初期設定 self.mainView.menuButton.menu = UIMenu(title: "", options: .displayInline, children: actions) self.mainView.menuButton.showsMenuAsPrimaryAction = true self.mainView.menuButton.setTitle( self.selectedMenuType.rawValue, for: .normal ) } } class TestMenuView1: UIView { let hogeLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "1個目のViewだよ!" label.font = .boldSystemFont(ofSize: 32) label.textColor = .black label.textAlignment = .center return label }() let menuButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("UIMenuを出す", for: .normal) button.setTitleColor(.white, for: .normal) button.backgroundColor = .red button.layer.cornerRadius = 25 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .systemGray setComponent() setConstraint() } private func setComponent(){ [hogeLabel, menuButton].forEach{ addSubview($0) } } private func setConstraint(){ NSLayoutConstraint.activate([ hogeLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hogeLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hogeLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), menuButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -20), menuButton.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), menuButton.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } シンボルが追加された! 新しく600以上シンボルが追加されました。 こちらでDLできます。 そのほか 他にはTabが透明にできるようになった(まだ確認中) TableViewのstyleに新しいものが追加された(まだ確認中) UIColorにsystemBrownというものが追加された茶色かな? 終わりに 多分私がまとめた内容が今回の変更点が全てではないと思います。 しかし主要な部分は抑えられていると思います。 それにしてもUIButtonが大幅に進化しましたね。こちらに全て説明されているので見てみるのも良いと思います。 個人的に下からぴょこっとちっちゃいModalViewがライブラリを使わず楽にできるようになったのは良いと思いました。 iOS15のリリース日はまだ先(予想が2021年秋頃)とのことなので世の中のiOSエンジニアさんたちは余裕を持って対応できると思います。iOS13みたいに死ぬほど作業量が増えるような修正がなくてよかったです。 ここまで読んでいただきありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IOS15でのUIKitの変更箇所をまとめた。

はじめに どうも@kaneko77です。 WWDC2021が今週ありました。 月曜日の26時からだった為、定時で上がって速攻寝て25時半に起きてWWDCに備えていました。 ※WWDCのEvent参照 今回はハードウェアの発表がなかったのは残念でしたが、割と面白内容になっていて早起きした甲斐あったな〜と思いました。 発表が終わった後にBeta版入れて色々試したのでその中でiOS15の変更点を今回は共有していきたいと思います。 特に私が興味がある内容に焦点を当てて記事を書いてます。 ※SwiftUIについては触れないです。 ※まだまだ新しい技術なので情報がネットに出回っていなく共有できない箇所がありますのでそちらご了承いただけますと幸いです。 こんな方対象 iOS15で何ができるようになったか知りたい人 iOSエンジニアの方 環境 Xcode: Version 13.0 beta (13A5154h) iOSVersion: iOS15 Swift: 5.5 参考にしたサイト https://blog.prototypr.io/new-in-ios-15-for-product-designers-design-engineers-aa504e3374f8 https://github.com/apple/swift/blob/main/CHANGELOG.md https://alejandromp.com/blog/wwdc21-notes/ https://developer.apple.com/videos/play/wwdc2021/10059/ 目次 UISheetPresentationControllerで下からピョコってするやつめちゃくちゃ楽になった! UIButtonで色々できるようになった UIMenuにサブビューが追加された シンボルが追加された! そのほか 紹介 UISheetPresentationControllerで下からピョコってするやつ楽になった! こちらは半Modalを簡単に実装できる物になります。 ライブラリだとこちらをよく使ってました。 以下がUISheetPresentationControllerを使ったmodal表示になります。 こちら参考に作ってます。 最初は半Modalで表示し、ボタンを押下すると普通のModalになるように作っています。 コード まずは簡単に実装したコードを貼ります。 こちら全部コピペしてもらえれば動きます。 ※iOS15からじゃないと使えないです。 表示Type(2種類) 呼び出し元 import UIKit class ViewController: UIViewController { var mainView: MainView! override func loadView() { super.loadView() self.mainView = MainView(frame: .zero) self.view = self.mainView } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.mainView.pushButton.addTarget(self, action: #selector(tapped(_:)),for: .touchUpInside) } // MARK: タッチイベント @objc func tapped(_ sender : Any) { present(ModalViewController.init(), animated: true, completion: nil) } } class MainView: UIView { let hogeLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "テストで表示" label.font = .boldSystemFont(ofSize: 32) label.textColor = .black label.textAlignment = .center return label }() let pushButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("「ぴょこ」っと出す", for: .normal) button.setTitleColor(.white, for: .normal) button.backgroundColor = .red button.layer.cornerRadius = 25 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .systemGray setComponent() setConstraint() } private func setComponent(){ [hogeLabel, pushButton].forEach{ addSubview($0) } } private func setConstraint(){ NSLayoutConstraint.activate([ hogeLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hogeLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hogeLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), pushButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -20), pushButton.heightAnchor.constraint(equalToConstant: 50), pushButton.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), pushButton.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 呼び出し側 import UIKit class ModalViewController: UIViewController, UISheetPresentationControllerDelegate, ModalViewDelegate { var mainView: ModalView! var sheetPresentationController: UISheetPresentationController! override func loadView() { super.loadView() self.mainView = ModalView(frame: .zero) self.mainView.delegate = self self.view = self.mainView sheetPresentationController = presentationController as? UISheetPresentationController // modalのサイズ sheetPresentationController.delegate = self sheetPresentationController.selectedDetentIdentifier = .medium // 上部にグラバーを表示する sheetPresentationController.prefersGrabberVisible = true // 高さの定義 sheetPresentationController.detents = [ .medium(), .large() ] // 角丸数値 let cornerRadius = 80 // modalの角丸の数値反映 self.sheetPresentationController.setValue(cornerRadius, forKey: "preferredCornerRadius") } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // MARK: ModalViewDelegate func onTapExpansionButton() { self.sheetPresentationController.animateChanges { if self.sheetPresentationController.selectedDetentIdentifier == .medium { self.sheetPresentationController.selectedDetentIdentifier = .large } else { self.sheetPresentationController.selectedDetentIdentifier = .medium } } } } protocol ModalViewDelegate: AnyObject { func onTapExpansionButton() } class ModalView: UIView { weak var delegate: ModalViewDelegate? let hugaLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "テストでModalを表示する" label.textColor = .black label.textAlignment = .center return label }() let expansion: UIButton = { let button = UIButton() button.setTitle("拡大・縮小", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false button.setTitleColor(.white, for: .normal) button.backgroundColor = .black button.layer.cornerRadius = 16 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .yellow setComponent() setConstraint() } // MARK: タッチイベント @objc func onTapExpansionButton(_ sender : Any) { delegate?.onTapExpansionButton() } private func setComponent(){ [hugaLabel, expansion].forEach{ addSubview($0) } self.expansion.addTarget(self, action: #selector(onTapExpansionButton(_:)),for: .touchUpInside) } private func setConstraint(){ NSLayoutConstraint.activate([ hugaLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hugaLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hugaLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), expansion.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -50), expansion.heightAnchor.constraint(equalToConstant: 32), expansion.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), expansion.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 説明 重要なのはここ! コメントに書いた内容となっております。 Modalの角丸の数値は自由に変更して遊んでみてください。 // modalのサイズ sheetPresentationController.delegate = self sheetPresentationController.selectedDetentIdentifier = .medium // 上部にグラバーを表示する sheetPresentationController.prefersGrabberVisible = true // 高さの定義 sheetPresentationController.detents = [ .medium(), .large() ] // 角丸数値 let cornerRadius = 80 // modalの角丸の数値反映 self.sheetPresentationController.setValue(cornerRadius, forKey: "preferredCornerRadius") あとはボタンを押下した時の処理 sheetPresentationController.selectedDetentIdentifierで自分の設定した高さを持っています。 その為、こちらを変更すればあらびっくり変わります。 sheetPresentationController.animateChangesはアニメーション処理です func onTapExpansionButton() { self.sheetPresentationController.animateChanges { if self.sheetPresentationController.selectedDetentIdentifier == .medium { self.sheetPresentationController.selectedDetentIdentifier = .large } else { self.sheetPresentationController.selectedDetentIdentifier = .medium } } } UIButtonで色々できるようになった こちら参考 こちらも参考 UIButtonで複数行がサポートされるようになった! これまではUIButtonで複数行のテキストを表示するとなると アトリビュート を使っていたと思います。 しかし今回から楽になっちゃいました✌️ また、角丸などもcornerRadiusで定義せずとも自動でできます。 コード 複数行に対応したボタン let pushButton: UIButton = { // configの定義 var config = UIButton.Configuration.tinted() // 画像の設定 // config.image = UIImage(systemName: "person.2.circle.fill") // 画像どっち側に表示するか (何種類かあります。) // config.imagePlacement = .top config.buttonSize = .large // 角丸を自動で出す (何種類かあります。) config.cornerStyle = .capsule // 背景の設定 config.baseBackgroundColor = .systemGreen let button = UIButton(configuration: config) button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("「ぴょこ」っと出す\nひょっこりはん!!", for: .normal) button.setTitleColor(.white, for: .normal) return button }() 上記のコードを適当な画面に入れることで下記のように表示されると思います。 ちなみに画像などの設定をすると以下のように表示されます? UIButtonでインジケータの表示ができるようになった これまではライブラリなどを入れてログインボタンを押下したらAPIが走ってその際はクルクルさせるみたいなのをやっていましたが、Configでできるようになりました。 実演したのが以下になります。3秒でインジケータが終わるように書いています。 コード こちらshowsActivityIndicatorの ture or false で楽に制御できます。 画像だけクルクル回るのは気にくはない感じがある(ボソッ) // インジケーターの表示 self.mainView.pushButton.configuration?.showsActivityIndicator = true UIMenuにサブビューが追加された 多分UIMenuってIOS13からいますよね? 割と利用シーンがわからず放置していました。ActionSheet使うわってなっていましたが、 サブビュー追加は割と私の中では使ってみたいなと思いました。 ではいきましょう。 元々は2階層のものは以下のような表示でした(iOS14.5) しかしiOS15から以下になります。 階層があるものは一発でわかるようになります UIが進化して使いやすくなりました。? コード 適当に作りました。 iOS15になったからと言ってこの階層の設定については..... 何もやることがありません もう一回言います。勝手にOSがやってくれるます。その為 何もやることはありません import UIKit // メニュー表示項目 enum MenuType: String { case A = "アニメ" case B = "食べ物" case C = "電話" case D = "スターウォーズ" case E = "ゴーストバスター" } class TestMenuViewController1: UIViewController { var mainView: TestMenuView1! var selectedMenuType: MenuType = .A override func loadView() { super.loadView() self.mainView = TestMenuView1(frame: .zero) self.view = self.mainView } override func viewDidLoad() { super.viewDidLoad() // UIMenuを設定 self.menuSet() } private func menuSet() { var actions = [UIMenuElement]() [MenuType.A, MenuType.B, MenuType.C].forEach{ element in let action = UIAction( title: element.rawValue, state: self.selectedMenuType == element ? .on : .off, handler: { _ in self.selectedMenuType = element // stateの更新 self.menuSet() }) actions.append(action) } var subMenus = [UIAction]() [MenuType.D, MenuType.E].forEach{ element in let action = UIAction( title: element.rawValue, state: self.selectedMenuType == element ? .on : .off, handler: { _ in self.selectedMenuType = element // stateの更新 self.menuSet() }) subMenus.append(action) } let subMenu = UIMenu(title: "映画", children: subMenus) actions.append(subMenu) // Buttonに対してUIMenuの初期設定 self.mainView.menuButton.menu = UIMenu(title: "", options: .displayInline, children: actions) self.mainView.menuButton.showsMenuAsPrimaryAction = true self.mainView.menuButton.setTitle( self.selectedMenuType.rawValue, for: .normal ) } } class TestMenuView1: UIView { let hogeLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "1個目のViewだよ!" label.font = .boldSystemFont(ofSize: 32) label.textColor = .black label.textAlignment = .center return label }() let menuButton: UIButton = { let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("UIMenuを出す", for: .normal) button.setTitleColor(.white, for: .normal) button.backgroundColor = .red button.layer.cornerRadius = 25 return button }() override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .systemGray setComponent() setConstraint() } private func setComponent(){ [hogeLabel, menuButton].forEach{ addSubview($0) } } private func setConstraint(){ NSLayoutConstraint.activate([ hogeLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 50), hogeLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), hogeLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), menuButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -20), menuButton.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 20), menuButton.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -20), ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } シンボルが追加された! 新しく600以上シンボルが追加されました。 こちらでDLできます。 そのほか 他にはTabが透明にできるようになった(まだ確認中) TableViewのstyleに新しいものが追加された(まだ確認中) UIColorにsystemBrownというものが追加された茶色かな? 終わりに 多分私がまとめた内容が今回の変更点が全てではないと思います。 しかし主要な部分は抑えられていると思います。 それにしてもUIButtonが大幅に進化しましたね。こちらに全て説明されているので見てみるのも良いと思います。 個人的に下からぴょこっとちっちゃいModalViewがライブラリを使わず楽にできるようになったのは良いと思いました。 iOS15のリリース日はまだ先(予想が2021年秋頃)とのことなので世の中のiOSエンジニアさんたちは余裕を持って対応できると思います。iOS13みたいに死ぬほど作業量が増えるような修正がなくてよかったです。 ここまで読んでいただきありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】タップした時に追従してくるViewを作成する

はじめに このような感じで、タップした位置に追従してくるようなViewを作成したいと思います。 実装 コピペで作成できます。 final class ViewController: UIViewController { private var myView: UIView! override func viewDidLoad() { super.viewDidLoad() myView = UIView() myView.frame.size = CGSize(width: 100, height: 100) myView.center = self.view.center myView.backgroundColor = .red myView.isUserInteractionEnabled = true self.view.addSubview(myView) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if touches.first!.view == self.view { let after = touches.first!.location(in: self.view) let before = myView.center let deltaX = after.x - before.x let deltaY = after.y - before.y UIView.animate(withDuration: 0.3) { self.myView.transform = CGAffineTransform(translationX: deltaX, y: deltaY) } } } } 解説 touchesBeganはタップした時に呼ばれるメソッドです。(touchesBegan) 一番はじめにタップした箇所の座標を取得します。これがViewの目的地の座標というわけです。 let after = touches.first!.location(in: self.view) 移動前のViewの座標も取得する必要があります。 let before = myView.center xとyの変化量をそれぞれ求めます。(変化量というのは、どれぐらい値が増減したかを表すものです) 例えば、移動前の座標(10, 300)から移動後の座標(30, 200)に移動した場合のxとyの変化量Δx, Δyは Δx = 30 - 10 = 20 Δy = 200 - 300 = -100 よって、変化量は(20, -100)となるわけです。 let deltaX = after.x - before.x let deltaY = after.y - before.y そして、求めた変化量がViewが移動するべき移動量になるので、CGAffineTransformでアニメーション付きで移動させてあげます。(CGAffineTransform) UIView.animate(withDuration: 0.3) { self.myView.transform = CGAffineTransform(translationX: deltaX, y: deltaY) } おわりに おわりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

手軽く iOS push 通知

Bark というアプリを利用 ダウンロード: ソースはこちら サーバーのソースはこちら サーバーは Golang 製なので、自分は Golang 分からない。Java で実装可能かを試したらできました。 iOS push 通知必要な情報はここに公開されている: Key ID: LH4T9V5U4R TeamID: 5U8LBRXG3A AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8 ← ファイルをダウンロード プッシュサーバーの実装はこちらを参考 利用するライブラリーはこちら 尚、プッシュするために、お持ちの iOS デバイストークンが必要なので、Docker で上記のサーバーを起動し、iOS 端末から自分のサーバーを追加し、1回プッシュ通知を試す。 そうすると、bark-data のフォルダに、bark.db というファイルに、デバイストークンが保存されているので、boltbrowser というソフトで見ることができる。 push server の java ソース(pushy ほぼそのまま) .p8 ファイルは、作者のサイトからダウンロード。有効期限は無限らしい。 package jp.co.syslinks.ios_push; import java.io.File; import java.util.concurrent.ExecutionException; import com.eatthepath.pushy.apns.ApnsClient; import com.eatthepath.pushy.apns.ApnsClientBuilder; import com.eatthepath.pushy.apns.PushNotificationResponse; import com.eatthepath.pushy.apns.auth.ApnsSigningKey; import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import com.eatthepath.pushy.apns.util.TokenUtil; import com.eatthepath.pushy.apns.util.concurrent.PushNotificationFuture; public class App { public static void main(String[] args) throws Exception { final String teamID = "5U8LBRXG3A"; // 作者サイトから取得 final String keyId = "LH4T9V5U4R"; // 作者サイトから取得 final String topic = "me.fin.bark"; // ソースから取得 final String tokenString = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // iOS デバイストークン。docker の bark.db から取得 final ApnsClient apnsClient = new ApnsClientBuilder() // .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) // pro ★★★ここ注意、本番環境で設定★★★ //.setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST) // dev //.setMetricsListener(new MyCustomMetricsListener()) // dev .setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File("AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8"), teamID, keyId)) // p8File, TeamID, Key ID .build(); final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder(); payloadBuilder.setAlertBody("Example!"); //本文 payloadBuilder.setAlertTitle("Title!"); //タイトル payloadBuilder.setSound("minuet.caf"); //サウンド設定 SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(TokenUtil.sanitizeTokenString(tokenString), topic, payloadBuilder.build()); final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>> sendNotificationFuture = apnsClient.sendNotification(pushNotification); try { final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture.get(); if (pushNotificationResponse.isAccepted()) { System.out.println("accepted"); } else { System.out.println("rejected: " + pushNotificationResponse.getRejectionReason()); pushNotificationResponse.getTokenInvalidationTimestamp().ifPresent(timestamp -> { System.out.println("\t…invalid " + timestamp); }); } } catch (final ExecutionException e) { System.err.println("Failed"); e.printStackTrace(); } sendNotificationFuture.whenComplete((response, cause) -> { if (response != null) { // Handle the push notification response as before from here. } else { // Something went wrong when trying to send the notification to the // APNs server. Note that this is distinct from a rejection from // the server, and indicates that something went wrong when actually // sending the notification or waiting for a reply. cause.printStackTrace(); } }); apnsClient.close(); /*final CompletableFuture<Void> closeFuture = apnsClient.close(); closeFuture.whenComplete((response, cause) -> { if (response != null) { } else { cause.printStackTrace(); } });*/ } } これで、iOS 端末に、通知がいくようになる。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】UITextViewにプレースホルダーをつける

はじめに テキストビューにもテキストフィールドのようにプレースホルダーをつけたい時があるかと思います。 なので、今回はテキストビューにプレースホルダーをつけるにはどうすればいいのかを解説します。 完成するもの GitHub 実装 まずは、PlaceTextViewというカスタムクラスを作るためのswiftファイルとxibファイルを作成してください。 コードを書いていきます。 final class PlaceTextView: UITextView { var placeHolder: String = "" { willSet { self.placeHolderLabel.text = newValue self.placeHolderLabel.sizeToFit() } } private lazy var placeHolderLabel: UILabel = { let label = UILabel() label.lineBreakMode = .byWordWrapping label.numberOfLines = 0 label.font = self.font label.textColor = .gray label.backgroundColor = .clear label.translatesAutoresizingMaskIntoConstraints = false self.addSubview(label) return label }() override func awakeFromNib() { super.awakeFromNib() NotificationCenter.default.addObserver(self, selector: #selector(textDidChanged), name: UITextView.textDidChangeNotification, object: nil) NSLayoutConstraint.activate([ placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 7), placeHolderLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 7), placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5), placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 5) ]) } @objc private func textDidChanged() { let shouldHidden = self.placeHolder.isEmpty || !self.text.isEmpty self.placeHolderLabel.alpha = shouldHidden ? 0 : 1 } } Main.storyboardでUITextViewを配置します。 配置したUITextViewのカスタムクラスに先ほど書いたカスタムクラスを継承させます。n ViewControllerではどのようなプレースホルダーを表示させるのかを設定します。 final class ViewController: UIViewController { @IBOutlet private weak var textView: PlaceTextView! override func viewDidLoad() { super.viewDidLoad() textView.placeHolder = "入力してください。" } } 解説 表示させるプレースホルダーのテキストをこちらで指定し、入ってきた値をラベルのテキストにします。 var placeHolder: String = "" { willSet { self.placeHolderLabel.text = newValue self.placeHolderLabel.sizeToFit() } } ラベルの設定をします。 private lazy var placeHolderLabel: UILabel = { let label = UILabel() label.lineBreakMode = .byWordWrapping label.numberOfLines = 0 label.font = self.font label.textColor = .gray label.backgroundColor = .clear label.translatesAutoresizingMaskIntoConstraints = false self.addSubview(label) return label }() オブザーバーを追加します。UITextView.textDidChangeNotificationなので、このオブザーバーはテキストが入力または消去された時に通知されます。 NotificationCenter.default.addObserver(self, selector: #selector(textDidChanged), name: UITextView.textDidChangeNotification, object: nil) 先ほど追加したオブザーバーで通知されるたびに呼ばれるメソッドです。 初回で入力されたまたは全てテキストが消去されたときにalphaを変更することで表示非表示を切り替えます。 @objc private func textDidChanged() { let shouldHidden = self.placeHolder.isEmpty || !self.text.isEmpty self.placeHolderLabel.alpha = shouldHidden ? 0 : 1 } オートレイアウトの指定です。これを指定しないと左上つめつめになってしまうので余白として制約を貼りました。 NSLayoutConstraint.activate([ placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 7), placeHolderLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 7), placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5), placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 5) ]) あとはViewControllerでIBOutlet接続をしてプレースホルダーを設定してあげましょう。 final class ViewController: UIViewController { @IBOutlet private weak var textView: PlaceTextView! override func viewDidLoad() { super.viewDidLoad() textView.placeHolder = "入力してください。" } } おわりに 公式でサポートしてほしいですね、、、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WIP] WWDC2021のCoding & Designチャレンジをしながらの学びメモ

この記事の注意点 学んだことをアウトプットするというより備忘録的要素強めのメモになります スクリーンショットや動画など、WWDCには開発者としてのNDAがつきものですが、この記事はあくまで全員が見れる情報に関してのみの記述になります また今回のWWDCのCoding & Designチャレンジには公式に#WWDC21Challengesというハッシュタグが用意されています。そのため、このハッシュタグにて公開されているキャプチャと同等レベルであればNDAには抵触しないとみなします。 WWDC2021にはたくさんのチャレンジが用意されていますが、今回は独断と偏見でそのうちの一部にだけ挑戦しています。その他のチャレンジが気になる方はぜひWWDC公式サイトなどから見てみてください。 かなりボリューミーになることが予想されるためWIPをつけている間は複数回に分けて更新していきます。 挑戦したチャレンジ 全チャレンジはこちら サイトはアルファベット順になっていますが、今回はDeveloperアプリの順番でやっていきます。 Build an app that recognizes custom audio through ShazamKit Create your first 3D model with Object Capture Animated artistry in SwiftUI Throwback with SwiftUI Framework Freestyle Memgraph capture the flag Focus on Focus in SwiftUI Prototype with SwiftUI Build an app that recognizes custom audio through ShazamKit ShazamKit ... Shazamのカタログやカスタムカタログで音楽聞き取りができる Signatureというものを音から抽出してそのマッチングを行う SHSession, SHSignatureGenerator Delegateでマッチしたかどうかの結果を受け取る SHMediaLibraryを使ってShazamライブラリに追加できる AppleMusicとの連携をしたければMusicKitを使う サンプルコード SHSignatureGeneratorにバッファーを渡すと音声がsignatureになる Part1からPart3まであるのに違いがない...これはなんだ...そしてiPadだけ対応のサンプルだった... ビデオの特定の音声をsignature化しておくことで音声に合わせてクイズを出題するアプリが作れる SwiftUI的なフォルダ構成はApp/Data/Extensions/Resources/Views シミュレータでも実行可能 任意の状態から音でアプリの状態を変えることができる、Shazamってそういう技術だけど応用例があるとすごい 実際に使えそうなシチュエーション 自分でやるのはデータを用意するのが必要だし時間かかるなと思ったのでアイデア出しだけ サンプルのような教材動画の副教材アプリ 脱出ゲームのギミック(部屋を進んでいくと流れている音声が変わる→制限時間の同期や音楽を使った謎など) インタラクティブ広告(アプリを触っている最中、特定の場所にいくとそこの音で広告がポップアップする→自然とそのお店に入ってみようかなという動機付けに使えるのでは?) Create your first 3D model with Object Capture サンプルアプリ こっちは全部ベタ書きだな 3Dモデルを作るにはmacOSも必要らしい iOS: PhotogrammetrySession(Image Depth Gravityほか)→Connect output stream RealityKitで扱う Model File(URL), ModelEntity, BoundingBox(RealityKit) 椅子にGameSir T4Proを置いて撮ってみた、HEIC, TXT, TIFファイルで構成されるみたい、iPhoneのドキュメントから確認できる 3Dモデル化はMonteleyが必要らしいので用意できたらやります Animated artistry in SwiftUI ignoresSafeArea(edges:) .background(.regularMaterial), .thinMaterial .foregroundStyle(.secondary) .safeAreaInset(edge: .bottom) GeometryReader, drawingGroup() withAnimation Canvas { context, size in } Canvasのかきごこち、だいぶp5とかに近そう context.fillとかある CanvasをTimelineViewで囲ってあげるとアニメーションもできそう .accessibilityLabelもつけてあげる大事さ やってみたものはこちら 慣れは必要だけど感覚としてはProcessing的な書き方をSwiftでもできるようになったという感じ Throwback with SwiftUI 指示通り乱数くじ引きをしたところ1989年のUIを再現することに(生まれてない...笑) ref: https://www.youtube.com/watch?v=IfxTRyrEUvo 1989年はMachintosh Portableの発売年、ノートパソコンの走りのようなものだったのかな (特に参照セッションがないので時間ができた時に作って追記します) Framework Freestyle サンプルから100行以内のコードと新しいフレームワークを組み合わせて何かを作ってあげるチャレンジ (こちらも参照セッションがないので時間ができた時に作って追記します) Memgraph capture the flag 慣れない分野のせいでめっちゃ雑メモです。検索かかってしまったら申し訳ないです... symbolication atosツール デバッグしたときに原因を特定しやすくする dSYM translate runtime addresses to file addresses Linkerの挙動理解な Headerと __TEXTと __DATA otool -lとgrepを使う ASLR slide, S=L-A vmmapツール -tVオプション atosの-oと-lオプション あんまりわかってないけど途中のまとめ App binaries and frameworks are Mach-O files Kernel adds ASLR slide to determine load address ASLR slide equation DWARFはいろんな情報を含められる symbols -onlyFuncStartsData nmコマンド Stripビルドセッティング
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】モバイルアプリのビルド番号を実行時に取得するライブラリを公開しました

はじめに モバイル向けアプリを作っている時、現在実機にインストールされているバージョンを起動画面や設定画面に表示することがあると思います。バージョンの場合はApplication.versionで取得できるので簡単に実装できるのですが、ビルド番号を実機で取得する方法は用意されていません。 そこで、実機でのビルド番号取得を簡単に実現するライブラリを作成したので紹介したいと思います。 ちなみにEditorの場合は下記のように取得できるのですが、もちろん実機では動作しません。 // バージョン PlayerSettings.bundleVersion; // ビルド番号 PlayerSettings.iOS.buildNumber; PlayerSettings.Android.bundleVersionCode; 想定している用途 よくあるパターンとして、CIでビルドするたびにセマンティクスバージョニングのバッチバージョンへビルド番号に反映させることあります(e.g. v1.0.[ビルド番号]) 。この方法ですとバージョン表記だけでどこまでの変更が反映されているか判断できるのでビルド番号は必要ないのですが、アプリバージョンを頻繁に変えたくない場合もあると思います。 バージョン表記を綺麗に保ちたい場合などが多いと思うのですが、今回僕が必要だと思ったきっかけはTest Flightの関係です。Test Flightでは外部テスターにアプリを配布する際に簡易的な審査(1日程度)が挟まるのですが、同一バージョンは審査されない仕様を利用すると即時配布することが可能になります。この仕組みを使うためにバージョンを変えなくなかったのですが、実機にインストールされているバージョンが分からなくなってしまうためビルド番号を併記したいという状況になりました。 免責事項 プラットフォーム毎に呼び方が違いますが、この記事では下記のような使い分けをしています。 バージョン (Version) iOS: CFBundleShortVersionString Android: versionName ビルド番号 (Build Number) iOS: CFBundleVersion Android: versionName 導入方法と使い方 READMEに書いてある通りですが、簡単に日本語でも紹介します。 導入方法 Unity 2019.3.4f1, Unity 2020.1a21以降で使えるPackage Managerの機能を使って配布しています。 Unityプロジェクトを開く Window -> Package Managerを開く 左上の"+"ボタンから"Add package from git URL..."をクリック https://github.com/nkjzm/UniBuildNumber.git?path=Assets/UniBuildNumberを入力し、"Add"をクリック 使い方 下記の3つのstaticなメソッドを用意してあります。iOS/Androidで型が違うので注意してください。 // Get build number in iOS string buildNumber_ios = nkjzm.UniBuildNumber.GetIOSBuildNumber(); // Get build number in Android int buildNumber_android = nkjzm.UniBuildNumber.GetAndroidVersionCode(); // Get build number in current platform (iOS or Android) string buildNumber = nkjzm.UniBuildNumber.GetCurrentBuildNumber(); 僕の場合は下記のように使用しています。 private void Start() { versionLabel.text = $"{Application.version} ({UniBuildNumber.GetCurrentBuildNumber()})"; } 参考 今回のライブラリは下記の記事を参考にライブラリを作成・公開させていただきました。ありがとうございます。 最後に 普段はTwitterでUnityやxR技術の情報発信をしているので、良かったらフォローしていただけると嬉しいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】PageViewControllerで画面遷移する

はじめに 今回はPageViewControllerを使ってスワイプした時に画面遷移をさせてみます。 GitHub 実装 一番左がPageViewControllerで、赤色がFirstViewController, 青色がSecondViewController, 緑色がThirdViewControllerです。 そして、PageViewControllerというUIPageViewControllerを継承したクラスを作ります。 final class PageViewController: UIPageViewController 色のついたViewControllrにStoryboardIDとSwiftファイルを用意してください。 全体のコードは以下の通りです。 final class PageViewController: UIPageViewController { private var controllers = [UIViewController]() override func viewDidLoad() { super.viewDidLoad() setupPageViewController() } private func setupPageViewController() { let firstVC = storyboard?.instantiateViewController( withIdentifier: "FirstViewController" ) as! FirstViewController let secondVC = storyboard?.instantiateViewController( withIdentifier: "SecondViewController" ) as! SecondViewController let thirdVC = storyboard?.instantiateViewController( withIdentifier: "ThirdViewController" ) as! ThirdViewController controllers = [firstVC, secondVC, thirdVC] setViewControllers([controllers[0]], direction: .forward, animated: true, completion: nil) self.dataSource = self } } extension PageViewController: UIPageViewControllerDataSource { func presentationCount(for pageViewController: UIPageViewController) -> Int { return controllers.count } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { if let index = controllers.firstIndex(of: viewController), index < controllers.count - 1 { return controllers[index + 1] } return nil } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { if let index = controllers.firstIndex(of: viewController), index > 0 { return controllers[index - 1] } return nil } } 解説 表示するためのViewControllerを配列に格納して管理します。 let firstVC = storyboard?.instantiateViewController( withIdentifier: "FirstViewController" ) as! FirstViewController let secondVC = storyboard?.instantiateViewController( withIdentifier: "SecondViewController" ) as! SecondViewController let thirdVC = storyboard?.instantiateViewController( withIdentifier: "ThirdViewController" ) as! ThirdViewController controllers = [firstVC, secondVC, thirdVC] // 最初の画面を設定 setViewControllers([controllers[0]], direction: .forward, animated: true, completion: nil) // ここでデリゲートも設定しておく self.dataSource = self setViewControllers(...)を使い、最初に表示する画面を設定します。 setViewControllers setViewControllers([controllers[0]], direction: .forward, animated: true, completion: nil) UIPageViewControllerDataSourceを見ていきます。 ページ数を返します。 func presentationCount(for pageViewController: UIPageViewController) -> Int { return self.controllers.count } 左スワイプ(Before)した時の表示する画面を返します。 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { if let index = controllers.firstIndex(of: viewController), index < controllers.count - 1 { return controllers[index + 1] } return nil } 右スワイプ(After)した時の表示する画面を返します。 func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { if let index = controllers.firstIndex(of: viewController), index > 0 { return controllers[index - 1] } return nil } おわりに PageViewController PageViewControllerを使った画面遷移をしてみました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む