20210411のiOSに関する記事は5件です。

【Swift】UICollectionViewCompositionalLayoutでレイアウトを組む

はじめに 今回はUICollectionViewCompositionalLayoutを使ってGridとInstagram風のレイアウトを組んでみたいと思います。 GitHub 以下のCollectionCompositeLayoutフォルダに今回のプロジェクトはあります。 カスタムセル実装 表示させるカスタムセルの背景色は以下のように設定しました。 CustomCollectionViewCell import UIKit final class CustomCollectionViewCell: UICollectionViewCell { static var identifier: String { return String(describing: self) } private let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, .cyan, .magenta ] override init(frame: CGRect) { super.init(frame: frame) contentView.backgroundColor = colors.randomElement() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } Gridレイアウト実装 まず、Gridの方から作ってみたいと思います。 GridViewController import UIKit final class GridViewController: UIViewController { @IBOutlet private weak var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() collectionView.collectionViewLayout = UICollectionViewCompositionalLayout( section: gridSection() ) collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.identifier) collectionView.dataSource = self } private func gridSection() -> NSCollectionLayoutSection { let itemCount = 3 let lineCount = itemCount - 1 let itemSpacing = CGFloat(2) let itemLength = (self.view.frame.size.width - (itemSpacing * CGFloat(lineCount))) / CGFloat(itemCount) //一つのitem let item = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(itemLength), heightDimension: .absolute(itemLength) ) ) //itemを三つ横並びに //.fractionalは親Viewとの割合 let items = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1) ), subitem: item, count: itemCount ) //item間のスペース items.interItemSpacing = .fixed(itemSpacing) //itemsが縦に並ぶグループを作成 let groups = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .absolute(itemLength) ), subitems: [items] ) //groupsからsectionを生成 let section = NSCollectionLayoutSection(group: groups) section.interGroupSpacing = itemSpacing return section } } extension GridViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 200 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.identifier, for: indexPath) as! CustomCollectionViewCell return cell } } Gridレイアウト解説 大切なところのみ解説したいと思います。 基本的な構成は以下の画像のようになっていて、Itemを決めてGroupを決めてSectionを決めてLayoutを設定すると言う流れになります。 それぞれ使う定数を事前に定義しておきます。 let itemCount = 3 let lineCount = itemCount - 1 let itemSpacing = CGFloat(2) let itemLength = (self.view.frame.size.width - (itemSpacing * CGFloat(lineCount))) / CGFloat(itemCount) 一つのアイテムのサイズを決めます。 absoluteとは絶対値と言う意味で、決まった値を幅にする設定にしています。 let item = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(itemLength), heightDimension: .absolute(itemLength) ) ) 次に、アイテムが三つ横並びになったグループを一つ作ります。 横並びのグループを作りたいので、horizontalを指定しています。 fractionalWidthやfractionalHeightとは親Viewにたいしてどれぐらいの割合で幅を決めるかと言う意味です。親ビューと同じにしたいのであれば、1(100%)にします。70%にしたい場合は.fractionalWidth(7/10)や.fractionalWidth(0.7)のようにします。 subItemは先ほど作成したItemdです。それをitemCount(=3)個並べていると言う意味です。 let items = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1) ), subitem: item, count: itemCount ) これは先ほど作ったグループ(=items)にitemSpacing(=2)だけスペースを入れる設定をしています。 items.interItemSpacing = .fixed(itemSpacing) そして先ほど作成したグループ(=items)を縦に並べる設定をします。 縦に並べたいので、verticalにします。 let groups = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .absolute(itemLength) ), subitems: [items] ) groupsからsectionを作成し、スペースを開けます。 let section = NSCollectionLayoutSection(group: groups) section.interGroupSpacing = itemSpacing return section そして、最後にこの関数(func gridSection() -> NSCollectionLayoutSectionf)をcollectionViewに設定します。 collectionView.collectionViewLayout = UICollectionViewCompositionalLayout( section: gridSection() ) Instagram風レイアウト実装 次に、Instagram風レイアウトを組んでみたいと思います。先ほどのGridレイアウトより少し難しくなりますが、基本は同じです。 final class InstagramViewController: UIViewController { @IBOutlet private weak var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() collectionView.collectionViewLayout = UICollectionViewCompositionalLayout( section: instagramSection() ) collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.identifier) collectionView.dataSource = self } private func instagramSection() -> NSCollectionLayoutSection { let itemSpacing = CGFloat(2) let smallItemLength = (self.view.frame.size.width - (itemSpacing * 2)) / 3 let largeItemLength = smallItemLength * 2 + itemSpacing // 小itemが縦に2つ並んだグループ let smallItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(smallItemLength), heightDimension: .absolute(smallItemLength) ) ) let smallItemVerticalGroup = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(smallItemLength), heightDimension: .absolute(largeItemLength) ), subitem: smallItem, count: 2 ) smallItemVerticalGroup.interItemSpacing = .fixed(itemSpacing) // 小itemが縦に2つ並んだグループを横に3つ並べたグループ let smallItemsGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitem: smallItemVerticalGroup, count: 3 ) smallItemsGroup.interItemSpacing = .fixed(itemSpacing) // 大item + 小item*2 のグループ let largeItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(largeItemLength), heightDimension: .absolute(largeItemLength) ) ) let largeItemLeftGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitems: [largeItem, smallItemVerticalGroup] ) largeItemLeftGroup.interItemSpacing = .fixed(itemSpacing) let largeItemRightGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitems: [smallItemVerticalGroup, largeItem] ) largeItemRightGroup.interItemSpacing = .fixed(itemSpacing) // 各グループを縦に並べたグループ let subitems = [largeItemLeftGroup, smallItemsGroup, largeItemRightGroup, smallItemsGroup] let heightDimension = NSCollectionLayoutDimension.absolute( //sectionの間隔は50 largeItemLength * CGFloat(subitems.count) + (itemSpacing * 50) ) // 高さの計算は後に追加するスペース分も足す let groups = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: heightDimension), subitems: subitems ) groups.interItemSpacing = .fixed(itemSpacing) let section = NSCollectionLayoutSection(group: groups) section.interGroupSpacing = itemSpacing return section } } extension InstagramViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 200 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.identifier, for: indexPath) as! CustomCollectionViewCell return cell } } Instagram風レイアウト解説 こちらも大切のところのみ解説します。 今回はitemのサイズが異なるのでそれぞれ設定します。 let itemSpacing = CGFloat(2) let smallItemLength = (self.view.frame.size.width - (itemSpacing * 2)) / 3 let largeItemLength = smallItemLength * 2 + itemSpacing 小さいアイテムが縦に2つ並んだグループを作成しています。 let smallItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(smallItemLength), heightDimension: .absolute(smallItemLength) ) ) let smallItemVerticalGroup = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(smallItemLength), heightDimension: .absolute(largeItemLength) ), subitem: smallItem, count: 2 ) smallItemVerticalGroup.interItemSpacing = .fixed(itemSpacing) そして、そのグループを横に三つ並べたグループを作ります。 let smallItemsGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitem: smallItemVerticalGroup, count: 3 ) smallItemsGroup.interItemSpacing = .fixed(itemSpacing) 次に、左側に大きいアイテムと小さいアイテムの組み合わせ、右側に大きいアイテムと小さいアイテムの組み合わせのグループを作成しています。 let largeItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .absolute(largeItemLength), heightDimension: .absolute(largeItemLength) ) ) let largeItemLeftGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitems: [largeItem, smallItemVerticalGroup] ) largeItemLeftGroup.interItemSpacing = .fixed(itemSpacing) let largeItemRightGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(largeItemLength) ), subitems: [smallItemVerticalGroup, largeItem] ) largeItemRightGroup.interItemSpacing = .fixed(itemSpacing) そして、作ったグループ三つを縦に並べたグループを作成します。 let subitems = [largeItemLeftGroup, smallItemsGroup, largeItemRightGroup, smallItemsGroup] let heightDimension = NSCollectionLayoutDimension.absolute( //sectionの間隔は50 largeItemLength * CGFloat(subitems.count) + (itemSpacing * 50) ) let groups = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: heightDimension), subitems: subitems ) groups.interItemSpacing = .fixed(itemSpacing) 最後に、このグループを一つのセクションとして返します。 let section = NSCollectionLayoutSection(group: groups) section.interGroupSpacing = itemSpacing return section Gridの時と同じように、collectionViewに設定すればInstagram風のレイアウト完成です。 collectionView.collectionViewLayout = UICollectionViewCompositionalLayout( section: instagramSection() ) おわりに UITableViewがいらない日が来るかもしれませんね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React JS × Onsen UI 入門

Onsen UIについて 公式ページ 以下公式サイトから抜粋 モバイルアプリの開発に特化したUIコンポーネントの集まり ネイティブなiOSとAndroidのデザインガイドに準拠したデザインと機能を持ってる 無料で利用でき、完全にオープンソースなソフトウェア Onsen UIが吸収する各OSのUIの違い 例えば、AndroidではタブがUIでは上にあることがスタンダードとなっていて、iOSではタブがUIでは下にあることがスタンダードとなっています。 Onsen UIではこの2つの異なるUIを同じソースコードで実装できます。 他にも、新規のスクリーンがUIに重なる場合、iOSのデフォルトでは右からスライドしてきますが、Androidでは下からフェードしつつ重なってきます。このような細かいインタラクションの表現もOnsen UIでは自動で吸収してくれます。 UserAgentでCSSやJSの切り替えを行っているので、ChromeのデベロッパーツールでもUserAgentoを切りかるだけでUIの表現が変わるのを確認できます。 React.js と Onsen UI Onsen UIが2.0になって、React JSと組み合わせて使えるようになりました。 ※Onsen UI1.0はAngular JS 1.*系のみサポート $ npm install -g create-react-app $ npx create-react-app my-app $ cd my-app $ yarn add onsenui react-onsenui --save-dev $ yarn start src/App.js import logo from './logo.svg'; import './App.css'; // ボタンコンポーネントのインポート import { Button } from 'react-onsenui'; // Onsen UIのCSSのロード import "onsenui/css/onsen-css-components.css" function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <Button modifier="outline"> button </Button> </header> </div> ); } export default App; 以下のように、いつものReactのデフォルトページにOnsen UIのボタンが追加されます。 Onsen UIを利用したナビゲーション Onsen UIを利用してページ遷移を実装します。 Navigationというコンポーネントを利用します。 src/App.js import logo from './logo.svg'; import './App.css'; // ボタンコンポーネントのインポート import { Button } from 'react-onsenui'; // Onsen UIのCSSのロード import "onsenui/css/onsen-css-components.css" import Block from './components/block' function App() { return ( <div key="App" className="App"> <header className="App-header"> <Block /> </header> </div> ); } export default App; src/components/block.js import React from 'react'; import {Navigator, Page, Button, Toolbar, BackButton} from 'react-onsenui'; import "onsenui/css/onsen-css-components.css" class MainPage extends React.Component { pushPage() { this.props.navigator.pushPage({component: SecondPage}); } render() { return ( <Page key="page1"> <Toolbar> <div className="center">Navigator</div> </Toolbar> <p style={{textAlign: 'center'}}> <Button onClick={this.pushPage.bind(this)}>Push page</Button> </p> </Page> ); } } class SecondPage extends React.Component { pushPage() { this.props.navigator.pushPage({component: SecondPage}); } popPage() { this.props.navigator.popPage(); } render() { return ( <Page key="page2"> <Toolbar> <div key="div1" className="left"><BackButton>Back</BackButton></div> <div key="div2" className="center">Another page</div> </Toolbar> <p style={{textAlign: 'center'}}> <Button key="button2" onClick={this.popPage.bind(this)}>Pop page</Button> </p> </Page> ); } } class Block extends React.Component { renderPage(route, navigator) { const props = route.props || {}; props.navigator = navigator; props.key = route.component.name; return React.createElement(route.component, props); } render() { return ( <Navigator key="nav1" initialRoute={{component: MainPage}} renderPage={this.renderPage} /> ); } } export default Block; 各OSでのUIの違い 以下のようにNavigationではOSでUIが異なります Androidでの見た目 iOSでの見た目
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutterのflutter_staggered_grid_viewについての注意喚起

※ この記事の内容はflutter_staggered_grid_view 0.3.4時点での情報です。アップデートに伴ってパッケージが改善され、この不具合が発生しなくなっている可能性があります。 flutter_staggered_grid_view Flutterで互い違いのいい感じのGrid Viewを実装するパッケージです。 pub.devのflutter_staggered_grid_viewのページより 困ったこと GridView.builderのように任意の数の要素について上の画像のようなGrid Viewを生成するStaggeredGridView.countBuilderという関数があります。 このGrid Viewの各要素の中に画像を入れたのですが、このとき読み込まれた画像のキャシュが開放されずにメモリリークでアプリがクラッシュするという現象が発生しました。 結論 flutter_staggered_grid_viewを使用する際には要素数が無限であるか、画像がGrid Viewの要素の中に含まれているかを確認し、両方を満たす場合にはパッケージの使用を避けましょう。 関連ページ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】iOSアプリのアップデート申請手順

Xcode側の手順 project の「TARGETS」のVersionとBuildを上げる ▼バージョンアップの参考▼ 1.0 → 2.0 : メジャーバージョン(大規模な改修の場合) 1.0 → 1.1 : マイナーバージョン(中規模な改修の場合) 1.0 → 1.0.1 : メンテナンスバージョン(小規模な改修の場合)   2. 下記の記事の「⑦ アプリのアップロード」の手順通りにアプリをアップロードする https://qiita.com/Labi/items/3b71b8f5ef065904c1de iTunes Connect側の手順 iTunes Connectへアクセスして、マイAppを選択 https://itunesconnect.apple.com/ 2 アップデートするアプリのiOS App右の「+」ボタンを押下   3. 新規バージョンを入力して作成を押下   4. あとは初回リリースと同じように、必要な情報を入力して「審査へ提出」まで済ます。  審査へ提出までは、下記記事の⑥、⑧を参考。  https://qiita.com/Labi/items/3b71b8f5ef065904c1de  ⑥ App Store Connect にアプリの情報を登録  ⑧ 審査へ提出〜提出後の対応
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】ipaファイル作成手順 & ipaファイル公開手順

前書き ipaファイルを作成して、アプリをブラウザから端末にインストールする方法のメモ書き? ipaファイル作成手順 Generic iOS Device にする Product -> Archive を選択 Distribute App を選択 Ad Hoc を選択して、Nextを押下 デフォルトのままNextを押下 デフォルトのままNextを押下 Exportを押下して、任意の場所にipaファイルが格納されたフォルダを保存する ipaファイル公開手順 サーバーにipaインストール用のページを作成して、そこからipaをインストールできるようにします。 httpsで通信が可能なWebサーバーに下記で作成したファイルを配置します。 https://example.com |- ipa |- ipa.html |- ipa.plist |- XXXX.ipa   ipa.plistファイル(ファイル名はなんでも良い)を作成して、下記のコードをコピペして、任意の場所にファイルを置いてください。 ※ ipaファイルのURLはipaファイルが置かれているURLに変更してください ipa.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>http://example.com/XXXX.ipa</string> ← ipaファイルのurlを設定 </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>com.example.test</string> <key>bundle-version</key> <string>1.0.0</string> <key>kind</key> <string>software</string> <key>title</key> <string>AppName</string> </dict> </dict> </array> </dict> </plist>   2. ダウンロード用のHTMLファイルを作成して、下記のコードをコピペする。 ※ https://example.com/ はipa.plistが置かれている自分の環境のURLに変えてください。 ipa.html <a href="itms-services://?action=download-manifest&amp;url=https://example.com/ipa.plist">Download</a>   3. ipaファイルを設定したurl配下に配置してHTMLファイルを開いて、リンクを押下するとインストールできる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む