/PageMenuKitSwift

Paging menu controller like SmartNews, NewsPass, Gunosy, JCnews, NewsSuite, Netolabo and Hackadoll.

Primary LanguageSwift

PageMenuKitSwift

PageMenuController の Swift 版。

日本のニュース系アプリで使われている横スクロールのメニュー画面とそのコンテンツを表示するユーザインタフェースのクラス。 Xcode のプロジェクト一式を登録してあるので、実行すればシミュレータ上で動作確認が可能。

Swift で実装し直す際に、汎用的で拡張しやすいようにクラスを再設計した。ページメニューの見た目だけが違うので、スタイルごとに PMKPageMenuItem のサブクラスを実装し、それを利用する仕組みにした。よって、簡単にカスタムメニューを追加できる。

How to use PageMenuKit.framework

Xcode の Build Target に PageMenuKitXCFramework を指定して Build を実行すると、PageMenuKit.xcframework が作成される。これを自作アプリの Xcode の Project で設定する。

あとは、以下のようなコードを記述して利用する。

class RootViewController: UIViewController
{
  var pageMenuController: PMKPageMenuController? = nil

  override func viewDidLoad() {
    super.viewDidLoad()

    var controllers: [UIViewController] = []
    let dateFormatter = DateFormatter()
    for month in dateFormatter.monthSymbols {
      let viewController: DataViewController = DataViewController()
      viewController.title = month
      controllers.append(viewController)
    }

    let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
    /*
     * Available menu styles:
     * .plain, .tab, .smart, .hacka, .ellipse, .web, .suite, .netlab and .nhk
     * See PMKPageMenuItem.swift in PageMenuKit folder.
     *
     * menuColors: [] means that we will use the default colors
     * "startIndex" can be set 1...controllers.count.
     */
    pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .smart, menuColors: [], startIndex: 1, topBarHeight: statusBarHeight)
    self.addChild(pageMenuController!)
    self.view.addSubview(pageMenuController!.view)
    pageMenuController?.didMove(toParent: self)
  }
}

より詳細なコードは PageMenuKitDemo 内の RootViewController.swift を見てね。

Available Menu Styles

.plain

ニュースパス っぽいメニュー画面

.plain

.tab

グノシー っぽいメニュー画面

.tab

.smart

SmartNews っぽいメニュー画面

.smart

.hacka

ハッカドール っぽいメニュー画面

.hacka

.ellipse

JCnews っぽいメニュー画面

.ellipse

.web

JCnews のウェブサイト っぽいメニュー画面

.web

.suite

NewsSuite っぽいメニュー画面(背景色はグラデーション)

.suite

.netlab

ねとらぼ っぽいメニュー画面(背景色は透明)

.netlab

.nhk

NHK ニュース・防災 っぽいメニュー画面

.nhk

Examples

PMKPageMenuController の initializer の menuColors に [] を指定するとデフォルトの配色になる。ここでは、各スタイルごとに色を変更する例を示す。

.plain, .hacka, .ellipse, .nhk

.plain, .hacka, .ellipse, .nhk の各スタイルで指定できる色は一つだけである。 以下の例では .plain スタイルに 紫(.purple) を設定している。

pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .plain, menuColors: [ .purple ], topBarHeight: statusBarHeight)

.plain

.plain

.hacka

.hacka

.ellipse

.ellipse

.nhk

.nhk

.tab, .smart

.tab, .smart スタイルで指定できる色は一つ以上である。 以下の例では .tab スタイルに 赤、橙、黄、緑、青、紫 を設定している。 メニューの数が配色した数よりも多い場合は、順に色が適用される。

pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .tab, menuColors: [ .red, .orange, .yello, .green, .blue, .purple ], topBarHeight: statusBarHeight)

.tab

.tab

.smart

.smart

.web

.web スタイルも .tab, .smart スタイルと同様に指定できる色は一つ以上である。 ただし、背景色は現在固定されている。 また、現実装では最初に指定した色が境界線の色になる。

以下の例では .web スタイルに 赤、橙、黄、緑、青、紫 を設定している。 メニューの数が配色した数よりも多い場合は、順に色が適用される。

pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .web, menuColors: [ .red, .orange, .yello, .green, .blue, .purple ], topBarHeight: statusBarHeight)

.web

.suite

.suite スタイルで指定できる色は一つだけであるが、 現状ではインジケータの色が変更されるだけである。 以下の例では .suite スタイルに 青(.blue) を設定している。

pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .suite, menuColors: [ .blue ], topBarHeight: statusBarHeight)

.suite

.netlab

.netlab スタイルで指定できる色は一つだけである。 現状では、非選択時の文字色に影響する。 以下の例では .netlab スタイルに 赤(.red) を設定している。

pageMenuController = PMKPageMenuController(controllers: controllers, menuStyle: .netlab, menuColors: [ .red ], topBarHeight: statusBarHeight)

.netlab

Delegate Methods (optional)

ページの切り替え時に呼び出される Delegate を使うことも可能。

pageMenuController?.delegate = self

上記のような記述を追加して、必要に応じて以下のメソッドを実装してね。 現時点では、 .hacka スタイルのバッジ表示の際に利用しているだけ。

public protocol PMKPageMenuControllerDelegate: class
{
  // ページ画面上でスワイプ操作による切り替えが行われる前に呼び出される
  func pageMenuController(_ pageMenuController: PMKPageMenuController, willMoveTo viewController: UIViewController, at menuIndex: Int)
  // ページの切り替えが完了した際に呼び出される
  func pageMenuController(_ pageMenuController: PMKPageMenuController, didMoveTo viewController: UIViewController, at menuIndex: Int)

  // メニュー項目の作成などが完了した際に呼び出される
  func pageMenuController(_ pageMenuController: PMKPageMenuController, didPrepare menuItems: [PMKPageMenuItem])
  // メニューがタップされた際に呼び出される
  func pageMenuController(_ pageMenuController: PMKPageMenuController, didSelect menuItem: PMKPageMenuItem, at menuIndex: Int)
}

References

Qiita のニュース系アプリのユーザインタフェース PageMenuKit の実装 も見てね。カスタムメニューの実装方法についても書いてあるよ。

Requirements

  • Swift 5
  • iOS 13.6 or later
  • Xcode 12.3 or later

ToDo

  • .suite と .web と .netlab スタイルのカスタマイズ方法

License Agreement

Copyright (c) 2017-2020, Kouichi ABE (WALL) All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.