一部をくり抜いたオーバーレイを表示する方法
背景の一部を表示させたまま、半透明のオーバーレイを表示させたい事があったので、その時の実装した内容のメモです📝
具体的には下記のgifのような挙動の実現方法です。
ボタンをタップすると、背景に表示された緑の四角が見えるようにくり抜かれたオーバーレイを表示して、オーバーレイのどこかをタップすると、それが消えるというもの。
コードはこちらに置いてます。
一部をくり抜いたオーバーレイは OverlayClippedViewController
で実装していて、コードはこちら。
private lazy var backgroundLayer: CALayer = { let clippedViewWidth: CGFloat = 220 let clippedViewHeight: CGFloat = 220 let backgroundLayer = CALayer() backgroundLayer.bounds = view.bounds backgroundLayer.position = view.center backgroundLayer.backgroundColor = UIColor.black.cgColor backgroundLayer.opacity = 0.5 let maskLayer = CAShapeLayer() maskLayer.bounds = backgroundLayer.bounds let clippingViewRect = CGRect(x: (view.bounds.width - clippedViewWidth) / 2, y: 190, width: clippedViewWidth, height: clippedViewHeight) let path = UIBezierPath(roundedRect: clippingViewRect, cornerRadius: 5.0) path.append(UIBezierPath(rect: maskLayer.bounds)) maskLayer.fillColor = UIColor.black.cgColor maskLayer.path = path.cgPath maskLayer.position = view.center maskLayer.fillRule = CAShapeLayerFillRule.evenOdd backgroundLayer.mask = maskLayer return backgroundLayer }()
maskLayer
が塗りつぶしを指定するLayerで、 maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
が実際にくり抜く(塗りつぶさない)指定をしている箇所ですね。
半透明のLayerである backgroundLayer
のmaskにくり抜き用のmaskLayer
を指定する事で、該当箇所がくり抜かれた状態で表示できます✂️
あまり画像をクリップする等しないので、また同じような事を実現したくなった時のための備忘録でした✍️
DateFormatterを汎用化する
9月は山登りしたりテント泊したり、遅めの夏休みということで海外旅行🏝したり、リフレッシュできて満足しているmuchanです。
仕事とプライベートで割と慌ただしくしていた分、体調が心配だったけれども、毎日DHCのマルチビタミンを飲んでうがい手洗いをしていたら、風邪引くこともなく全力投球できました。やっぱり予防は大事ですね💁♀️
今回は割と自分のメモに近いですが、DateFormatterを汎用化する話。
DateをStringへ変換する際等によく利用するDateFormatterですが、DateFormatterを生成が重いという話をよく聞きますよね。計測している記事が参考になります👀
日時をDateの形で持っておいて、View側で日時を文字列に変換したい場合などには、ViewController側でDateFormatterを保持したりしますが、割と同じ形式の文字列に変換する事が多かったので、シングルトンで保持してどこからでも利用できるようにしました。
こんな感じ。
class MyDateFormatter { static let shared = MyDateFormatter() private let yyyyMMddDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.dateFormat = "yyyy/MM/dd" return dateFormatter }() private let HHmmDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.dateFormat = "HH:mm" return dateFormatter }() func formatToyyyyMMdd(from date: Date) -> String { return yyyyMMddDateFormatter.string(from: date) } func formatToHHmm(from date: Date) -> String { return HHmmDateFormatter.string(from: date) } }
利用する際は、以下のようになります。
let dateString = MyDateFormatter.shared.formatToyyyyMMdd(from: date)
よく使う形式のDateFormatterについては、シングルトンにして各処理から呼び出すようにする事で生成を1度で済ませることができ、処理を効率化できますね🙌
カスタムCellを使ったUITableView実装テンプレート
もう8月も終わりですね。夏の北アルプス登山は最高なのですが、今年はまだ行けてなくてウズウズしてます⛰
お気持ちが高まって、先週テント泊の練習に行ってきました。テント、最高です🏕
さて本題ですが、タイトル通りです。 TableViewを実装するシーンがよくあるので、コピペで使えるようにテンプレを作ってみました。 Xibで用意したカスタムCellを表示します。
ソースコードはこちら にあるので、気になった方は覗いてみてください。
実装内容を簡単に説明していきます。
UITableViewと制約追加
class CustomCellListViewController: UIViewController { private var sampleItems = [CustomCellItem]() private let tableView = UITableView(frame: .zero) override func viewDidLoad() { super.viewDidLoad() title = "CustomCellListViewController" view.addSubview(tableView) tableView.separatorStyle = .none tableView.delegate = self tableView.dataSource = self tableView.translatesAutoresizingMaskIntoConstraints = false tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true tableView.register(cellType: CustomCell.self) sampleItems = [ CustomCellItem(date: "2019/01/01", title: "あいうえお"), CustomCellItem(date: "2019/01/02", title: "かきくけこ"), CustomCellItem(date: "2019/01/03", title: "さしすせそ") ] } }
私は基本的に、UITableViewのframe をCGRect.zeroで初期化して、左右上下の制約を親のViewに合わせる形で実装しています。
そして セルの登録 tableView.register(cellType: CustomCell.self)
ですが、Extensionを書いて記述量を少なくしています。
public func register(cellType: UITableViewCell.Type, bundle: Bundle? = nil) { let className = String(describing: cellType) let nib = UINib(nibName: className, bundle: bundle) register(nib, forCellReuseIdentifier: className) }
こうしておくと、楽チンで良いですよ〜〜
カスタムCellの実装
カスタムCellは、Xibを利用して実装しています。
Xibの Custom Class に紐付けるClass(今回は CustomCell)を指定したのちに、ラベルなどの各コンポーネントをCustomCellクラスに紐付けていきます。
UITableViewDataSource, UITableViewDelegateに準拠する
Protocolに準拠する場合、私はよく、見通しをよくするために 対象ViewControllerのextension に書くようにしています。
extension CustomCellListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sampleItems.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = sampleItems[indexPath.row] let cell = tableView.dequeueReusableCell(with: CustomCell.self, for: indexPath) cell.update(item: item) return cell } }
ここの dequeueReusableCell(with: CustomCell.self, for: indexPath)
も、UITableViewのExtensionを書いて記述量を少なくしています。
public func dequeueReusableCell<T: UITableViewCell>(with type: T.Type, for indexPath: IndexPath) -> T { let className = String(describing: type) let cell = dequeueReusableCell(withIdentifier: className, for: indexPath) as? T guard let dequeueCell = cell else { fatalError("Could not dequeue a cell of class \(className)") } return dequeueCell }
そして、UITableViewDelegateはこんな感じ。
extension CustomCellListViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let viewController = UIViewController() viewController.view.backgroundColor = .white navigationController?.pushViewController(viewController, animated: true) tableView.deselectRow(at: indexPath, animated: true) } }
セルをタップした際は、次の画面に遷移するようにします。
戻ってきたときに、セルがグレー(選択状態)になってしまうのが格好悪いので tableView.deselectRow(at: indexPath, animated: true)
で選択状態を解除します。
以上、わたしのUITableView実装テンプレートの説明でした。 こういった、自分なりのテンプレートを用意しておくと、新しい画面の実装スピードも早くなって、良いですね〜🤸🏼♂️✨
ではでは、残りの夏も楽しみましょ〜🍧
ウー早く北アルプス行きたい...!!⛰⛰
UIColor Extention と #colorLiteralで色指定を楽にする
やっと夏っぽくなってきて嬉しいです。そして天気の子は最高です。
今日はUIColor をExtentionする話。 自分のサービスでよく使う色などは、共通化してどこからでも使えるようにしたい。そんな時にはUIColorを拡張すると良いですよね。
view.backgroundColor = UIColor.myColor.backgroudGray
こんな感じで指定できて、便利です😃
で、その時に#colorLiteralを使うとめっちゃ分かりやすいのです。
#colorLiteralを使うと
こんな感じで、色が一目瞭然👀 直感的ですね。 しかも、色の指定もGUI上でできて分かりやすい。
まず、colorLiteralはコード補完されます。
色を変えたいときは、色自体をダブルクリックして、otherを選びます。
すると色んな方法で色の指定ができるのです...!!便利だなぁ〜💓
という訳で、皆さんもUIColor Extention と #colorLiteral で色の管理をしてみてはいかがでしょうか〜!
OAuthSwiftでtwitterログイン認証を実装する
iOSでtwitter認証を実装する際によく利用されていた、公式ライブラリであるTwitterKitがサポート終了していたので、代替方法としてOAuthSwiftでtwitterのログイン認証を実装した。その際の手順をメモ。
準備
- ドキュメントに沿ってOAuthSwiftを入れる
- 今回はCarthage経由で入れた
- URL Schemesを設定する
- アプリ内WebViewで認証が終わった後に、指定されたURLでアプリに戻ってくるので、それをハンドリングする処理をAppDelegateに追加
実装
twitterログイン認証には、OAuth 1aを利用する必要がある。 OAuthSwiftにデモ実装の記述があるので、それをコピペすれば動いた。
最終的に私は以下のように実装。(ViewController内)
private lazy var oauthswift: OAuth1Swift = { return OAuth1Swift( consumerKey: "{ 自分のconsumerKeyを指定 }", consumerSecret: "{ 自分のconsumerSecretを指定 }", requestTokenUrl: "https://api.twitter.com/oauth/request_token", authorizeUrl: "https://api.twitter.com/oauth/authorize", accessTokenUrl: "https://api.twitter.com/oauth/access_token" ) }() # 事前にXibにボタンを追加、タップされた時にログイン処理を行うように @IBAction private func didTapTwitterLoginButton(_ sender: Any) { loginWithTwitter() } private func loginWithTwitter() { oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift) _ = oauthswift.authorize( withCallbackURL: URL(string: "oauth-swift://oauth-callback/twitter")!, success: { credential, response, parameters in # 成功した時の処理を記述 }, failure: { error in # 失敗した時の処理を記述 } ) }
Xcodeで画像リソースを追加する
iOSアプリ内で表示する画像ファイルの追加手順です。
公式ドキュメント
手順
Assets.xcassets
を選択- 画像をドラッグ&ドロップすると、その画像名でリソースが追加される
使い方
let imageView = UIImageView() imageView.image = #imageLiteral(resourceName: "home") # これでも読み込める let image = UIImage(named: "home")
おまけ
TabBarやNavigationBarなどのアイコンに画像を設定する場合、公式ガイドラインにサイズ指定の記述があるので、それに合わせたサイズの画像を指定する
ルート画面に下タブを追加する
UITabBarController
を利用することで、タブを表示することが可能。
前回の記事でrootViewControllerに指定した RootViewController
ファイルを以下のように書き換えると、2タブの画面が表示される。
今回の例では、NavigationControllerもセットしています。
画面表示
コード
import UIKit class RootViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() let firstViewController = UIViewController() firstViewController.view.backgroundColor = .white let firstNavigationController = UINavigationController(rootViewController: firstViewController) firstNavigationController.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 1) let secondViewController = UIViewController() secondViewController.view.backgroundColor = .white let secondNavigationController = UINavigationController(rootViewController: secondViewController) secondNavigationController.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 2) let viewControllers = [firstNavigationController, secondNavigationController] setViewControllers(viewControllers, animated: false) } }
ちなみに、好きなアイコンを指定したい、という場合は、tabBarItemを以下のように指定する。
firstNavigationController.tabBarItem = UITabBarItem(title: "ホーム", image: UIImage(named: "home"), tag: 1)
上記の場合、home.png
を事前に追加しているが、画像追加手順については Xcodeで画像リソースを追加するに記述。