SH Lab の アプリ開発部屋

リリースしたアプリの告知とかサポートとか、技術的なお話とか、そんな感じでつぶやきます。

tvOS向けアプリとフレンチトーストの作り方を解説してみる

AppleTVのアプリ開発していたらお腹が空いた

f:id:hoshi0523:20160508120855j:plain

自宅でtvOS向けアプリの開発していたら、無性にフレンチトーストが食べたくなったので、ノリで作ってみました。これがまずまずの美味しさだったので、アプリ作りフレンチトースト作りについて、備忘録を兼ねて解説していきたいと思います。フレンチトースト - Wikipedia

ちなみにアプリは、テレビ向けっぽく動画のアプリがよかったので、Youtube動画を再生できるアプリを作ってみます。

用意したものはこちら

f:id:hoshi0523:20160508120931j:plain

今回用意したのはこちらです(※1人分)。

  • パン → 1枚(厚手のもの)
  • バター → 少々
  • たまご → 1個
  • 牛乳 → 60cc
  • 砂糖 → スティックシュガー1本分
  • 開発機Macbook Pro
  • AppleTV → 第4世代
  • Siri RemoteiPhoneでも代用可能
  • USBケーブル → Type-C

バニラエッセンスがあると尚良いらしいですが、最寄りのスーパーに売ってなかったので今回は見送りました。

アプリの概要

今回作成したアプリはこちらに置いてあります。

github.com

作ろうとしているアプリの概要は、こんな感じです。

  1. あらかじめ、Youtubeのプレイリストを作成しておく
  2. アプリ起動時、Youtube API を使ってプレイリストを取得する
  3. 取得したプレイリスト内のサムネイルを、グリッド状に表示する
  4. 任意のサムネイルを選択すると、プレイヤー画面に遷移して再生開始

まずはプロジェクト作りから

複雑なアプリではないので、アプリのテンプレはSingle View Applicationを選択し、アプリ名をYoutubeViewerとしておきます。

f:id:hoshi0523:20160507234855p:plain

cocoapodsでライブラリを入れよう

cocoapodsを使って、必要なライブラリを入れておきます。Podfileはこんな感じで設定してみました。

platform :tvos, '9.0'
use_frameworks!

target 'YoutubeViewer' do
  pod 'HCYoutubeParser'
  pod 'Unbox'
  pod 'AlamofireImage'
end
  • HCYoutubeParser
    • Youtubeのコンテンツに対して、AppleTVでも再生可能な形式のURLを生成してくれるライブラリ。
  • Unbox
  • AlamofireImage
    • 画像の非同期取得ライブラリ
    • Alamofireを使うので、せっかくなのでこれも使おう


Unboxの使い方については、ギャップロさんを参考にさせてもらっていますよ。

www.gaprot.jp

また、フレンチトーストについては、こちらのサイトさんを参考にさせてもらっていますよ。

http://food.kihon.jp/breadtoast/1924food.kihon.jp

卵液を作ろう

f:id:hoshi0523:20160508121007j:plain:w600

パンに染み込ませるための卵液の作り方です。とは言っても、卵をよく溶いて、牛乳・砂糖(とバニラエッセンス)を加えて、しっかりと混ぜるだけです。

パンを浸そう

f:id:hoshi0523:20160508121029j:plain:w600

出来上がった卵液に、パンを浸します。

f:id:hoshi0523:20160508121035j:plain:w600

箸でつまむと、卵液をよく吸います。卵液が全て吸われてなくなるまで、両面をしっかりと浸しましょう。このまま放置するのも良いと思います。
それでも卵液が余るようなら、第2・第3のパンを投入しましょう。

Youtubeでプレイリストを作っておく

www.youtube.com

YoutubeViewer - YouTube

今回のアプリ内に並べるYoutubeのコンテンツは、プレイリストを作って用意することにします。適当なプレイリストを作成して、そのプレイリストのIDを控えておきましょう。

ちなみに自分は、今回のアプリ用にこんな感じのプレイリストを作ってみました。昔よく聞いていたバンドや照井春佳さん出演コンテンツ、なにかと所縁のあるコンテンツなどを並べてみました。特に先頭の動画はとてもカッコよろしいです。

このプレイリストのIDは PLrFwetrdOQ9ixp8Kj0mqBLKQAG9DTViJA だそうな。こちらはURLを見るとわかります。

YoutubeAPIを利用してプレイリストを取得する

まずはアプリからYoutubeAPIを実行して、プレイリストのデータを取得してみましょう。YoutubeAPIの仕様については、この辺りの公式ドキュメントを参考にするとよさそうです。

なお、YoutubeAPIを利用するには、Google API Key が必要です。API Keyは、各々で取得してください。以下のドキュメントから API キー → ブラウザ キー あたりを参考に取得すれば、とりあえず動作可能です。

APIの実行とJSONのパース(ViewController.swift)

API通信部分は、Alamofireを使って、こんな感じでメソッドとして定義しています。

// API実行用のメソッド.
private func requestPlaylist(completeHander: Response<AnyObject, NSError> -> Void) {
  // プレイリストを取得するためのURL.
  let url = "https://www.googleapis.com/youtube/v3/playlistItems"
  // リクエストに利用するパラメータ.
  var parameters = [String: String]()
  parameters["part"] = "snippet,contentDetails"
  parameters["maxResults"] = "50"
  parameters["playlistId"] = "PLrFwetrdOQ9ixp8Kj0mqBLKQAG9DTViJA"
  parameters["key"] = "自分の API Key を使ってね"
  // リクエスト実施.
  Alamofire
    .request(.GET, url, parameters: parameters)
    .responseJSON(completionHandler: completeHander)
}

viewDidLoad()あたりで、上記のメソッドを呼び出しています。

// APIを実行する.
self.requestPlaylist { [weak self] response in
  // レスポンスをパースする.
  if let data = response.data {
    do {
      self?.playlistItems = try Unbox(data) as PlaylistItems
    } catch let error {
      print("error = \(error)")
    }
  }
}

JSONデータのパースのためのDTO(YoutubePlaylistModel.swift)

受け取ったJSONデータの構造に対応するDTOを、ひたすら定義していきます。今回は、利用する項目が限られているので、不要な項目に対応する構造体やプロパティは、省略しております。

struct PlaylistItems {
  var items: [VideoItem]
}
struct VideoItem {
  let contentDetails: ContentDetails
  let snippet: Snippet
}
struct ContentDetails {
  let videoId: String
}
struct Snippet {
  let thumbnails: Thumbnails
}
struct Thumbnails {
  let mediumThumb: Thumbnail
}
struct Thumbnail {
  let url: NSURL
}

DTOを、Unboxableプロトコルに準拠させます。

extension PlaylistItems: Unboxable {
  init(unboxer: Unboxer) {
    self.items = unboxer.unbox("items")
  }
}
extension VideoItem: Unboxable {
  init(unboxer: Unboxer) {
    self.contentDetails = unboxer.unbox("contentDetails")
    self.snippet = unboxer.unbox("snippet")
  }
}
extension ContentDetails: Unboxable {
  init(unboxer: Unboxer) {
    self.videoId = unboxer.unbox("videoId")
  }
}
extension Snippet: Unboxable {
  init(unboxer: Unboxer) {
    self.thumbnails = unboxer.unbox("thumbnails")
  }
}
extension Thumbnails: Unboxable {
  init(unboxer: Unboxer) {
    self.mediumThumb = unboxer.unbox("medium")
  }
}
extension Thumbnail: Unboxable {
  init(unboxer: Unboxer) {
    self.url = unboxer.unbox("url")
  }
}

CollectionViewで、画面上にサムネイルを表示する

データの取得が成功したら、CollectionViewを使って画面にサムネイルを並べていきます。

f:id:hoshi0523:20160508130123p:plain

Storyboardでは、UICollectionViewを画面いっぱいに広げて配置しています。CellのクラスはYoutubeCellに変更し、サイズいっぱいにUIImageViewを配置しています。dataSource, delegate, IBOutlet の接続なども忘れずに行いましょう。

collectionViewの準備と表示処理(ViewController.swift)

通信の取得後に、collectionViewを更新する処理を追加します。

// APIを実行する.
self.requestPlaylist { [weak self] response in
  // 省略.
  // リクエスト取得後にcollectionViewをリロード.
  self?.collectionView.reloadData()
}

UICollectionView関連のプロトコルを、いい感じで実装しましょう。

extension ViewController: UICollectionViewDataSource {
  func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.playlistItems?.items.count ?? 0
  }
  func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(YoutubeCell.CellIdentifier, forIndexPath: indexPath) as! YoutubeCell
    cell.videoItem = self.playlistItems?.items[indexPath.item]
    return cell
  }
}
extension ViewController: UICollectionViewDelegate {
  func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    // TODO: 保留.
  }
}
extension ViewController: UICollectionViewDelegateFlowLayout {
  // セルの大きさ.
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return YoutubeCell.CellSize
  }
  // collectionView全体のInsets.
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 60, left: 90, bottom: 60, right: 90)
  }
  // セル間のスペースの最小値(Y軸方向).
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
    return 60 // 適当に.
  }
  // セル間のスペースの最小値(X軸方向).
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
    return 60 // 適当に.
  }
}

サムネイルの表示処理など(YoutubeCell.swift)

セルのクラスでは、画像の非同期取得処理と、フォーカス時の拡大処理などを実装しておきました。

import UIKit
import AlamofireImage
class YoutubeCell: UICollectionViewCell {
  static let CellIdentifier = "YoutubeCell"
  static let CellSize = CGSize(width: 320.0, height: 180.0)
  @IBOutlet weak var thumbnailImageView: UIImageView!
  var videoItem: VideoItem? {
    didSet {
      // URLが存在する場合は、画像を取得して当て込む.
      if let url = self.videoItem?.snippet.thumbnails.mediumThumb.url {
        self.thumbnailImageView.af_setImageWithURL(url, imageTransition: .CrossDissolve(0.5))
      }
    }
  }
  override func awakeFromNib() {
    super.awakeFromNib()
    // フォーカスが当たった時のアレ.
    self.thumbnailImageView.clipsToBounds = false
    self.thumbnailImageView.adjustsImageWhenAncestorFocused = true
  }
}

アプリを起動してみよう

ここまでくれば、画面上にプレイリストのサムネイルが表示されます。なかなかいい感じですね。「お?このPV知ってっぞ」と思った方は、なかなかのおっさんです。

f:id:hoshi0523:20160508131747p:plain:w600
f:id:hoshi0523:20160508133435g:plain:w600

パンを焼こう

ここまでくると、パンが卵液をしっかり吸い込んでいると思いますので、さっそくパンを焼いていきましょう。

f:id:hoshi0523:20160508135112j:plain:w600

フライパンにバターを入れ、中火で溶かしていきます。溶けたら弱火にしましょう。

f:id:hoshi0523:20160508135348j:plain:w600

パンを投入します。弱火のまま、じっくりと焼いていきましょう。

f:id:hoshi0523:20160508135446j:plain:w600

両面とも焼いていきます。こんがりきつね色の焦げ目がついたら、フレンチトーストは完成です!腹減ったね!

画面遷移を実装してみよう

プレイリストが取得できて、コンテンツが表示されるようになりました。あとは動画が再生できれば目標達成です。まずは「任意のコンテンツを選択したらその動画の情報を次の画面に渡す」という処理を実装してみます。

遷移先の画面を作成(PlayerViewController.swift)

プレイヤー用のViewControllerとしてPlayerViewControllerクラスを作成しておきます。AVKitをimportし、AVPlayerViewControllerを継承します。また、前の画面からコンテンツ情報を受け取る必要があるので、インスタンス変数としてVideoItemを定義しておきましょう。

import UIKit
import AVKit

class PlayerViewController: AVPlayerViewController {
  var videoItem: VideoItem?
}

storyboardで画面とsegueを追加

storyboard上では、AVPlayerViewControllerを配置して、トップ画面からsegueをつないでおきます。segueのidentifierは、適当に設定しておきましょう。配置したAVPlayerViewControllerのクラスは、先ほど作成したPlayerViewControllerに変更しておきましょう。

f:id:hoshi0523:20160508142252p:plain

コンテンツが選択された際の挙動(ViewController.swift)

トップ画面で任意のコンテンツを選択された際は、選択されたindexPathをもとにVideoItemを取得し、インスタンス変数として保持します。あとは、先ほど設定したsegueを呼び出して画面遷移を行います。

extension ViewController: UICollectionViewDelegate {
  func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    // 選択されたコンテンツを保持してプレイヤー画面へ.
    if let videoItem = self.playlistItems?.items[indexPath.item] {
      self.selectedVideoItem = videoItem
      self.performSegueWithIdentifier(ViewController.SEGUE_PLAY_MOVIE, sender: nil)
    }
  }
}

遷移のタイミングで呼ばれるメソッドで、PlayerViewControllerに対してVideoItemを渡します。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  // プレイヤー画面への遷移の場合.
  if segue.identifier == ViewController.SEGUE_PLAY_MOVIE {
    // 次の画面に、選択されたVideoItemを渡す.
    let playerViewController = segue.destinationViewController as! PlayerViewController
    playerViewController.videoItem = self.selectedVideoItem
  }
}

この辺りのロジックはいくらでもやりようがあるので、お好みで実装してくださいな。

動画の再生を行う

VideoItemを渡して再生画面まできたら、あとはURLをAVPlayerに渡せば再生開始となります。プレイリストに含まれるvideoIdから、AppleTVで再生可能なURLを生成するために、HCYoutubeParserというライブラリを利用します。iPhoneであれば、他にもWebViewを利用した再生も可能のなのですが、tvOSの場合はAVKitを使う必要があるため、この方法をで再生します。

import UIKit
import AVKit
import HCYoutubeParser
class PlayerViewController: AVPlayerViewController {
  var videoItem: VideoItem?
  override func viewDidLoad() {
    super.viewDidLoad()
    // videoIdを取り出す.
    guard let videoId = self.videoItem?.contentDetails.videoId else { return }
    // videoIdを元にコンテンツ情報を取得する.
    guard let dic = HCYoutubeParser.h264videosWithYoutubeID(videoId) else { return }
    // コンテンツのURLを取得して再生開始.
    if let urlString = dic["medium"] as? String, let url = NSURL(string: urlString) {
      self.player = AVPlayer(URL: url)
      self.player?.play()
    }
  }
}

HCYoutubeParserのメソッドで、videoIdからコンテンツURLを取得しています。取得したURLをAVPlayerに渡して再生を開始します。

f:id:hoshi0523:20160508153516p:plain:w600
f:id:hoshi0523:20160508153818p:plain:w600

ちゃんと再生できました!ちょっとわかりづらいですが、シークなども何もせずに利用可能になります。

tvOS向けアプリに関しては、動画アプリとはとても相性がいいと思うので、こんな感じで動画ビューワアプリを作ってみるのもいいかもしれないですね。

再生できない動画もあるみたい...?

作成したプレイリストのうち、再生できない動画が一定数ありました。試行回数もそれほどではないので定かではありませんが、どうやら古めのコンテンツについては再生ができないみたいでした。

アプリもフレンチトーストも完成した!

f:id:hoshi0523:20160508154750j:plain

というわけで、無事にアプリもフレンチトーストも完成しました。どちらもそれほど苦なく作成することが可能なので、よかったらお試しくださいませ!

swiftへのツッコミ、コード埋め込みやシンタックスハイライトへのツッコミ、調理へのツッコミ、撮影技術へのツッコミ等、お待ちしておりますm( _ _ )m

マンガ on ウェブ 第4号を配信!

あけましておめでとうございます!

みなさま、あけましておめでとうございます。
Susumu Hoshikawaでございます。
2016年もなにとぞよろしくおねがいいたしますm( _ _ )m

マンガ on ウェブ 第4号が配信開始♪

本日2016年1月1日から、Web雑誌 マンガ on ウェブ の 第4号 が配信開始となります!

f:id:hoshi0523:20150918203751p:plain

第4号は2冊で同時配信!

今号は、あまりにも特大ボリュームのため、side-A side-B の2冊同時配信となっております!

f:id:hoshi0523:20151231160045j:plainf:id:hoshi0523:20151231160053j:plain

お取り扱いはこれらのアプリにて!

第4号は、これらのアプリにて取り扱っております!

マンガ on ウェブ

マンガ on ウェブ

マンガ on ウェブ

  • Susumu Hoshikawa
  • ブック
  • 無料

各種マンガアプリ

海猿 -UMIZARU-

海猿 -UMIZARU-

  • Susumu Hoshikawa
  • ブック
  • 無料
特攻の島

特攻の島

  • Susumu Hoshikawa
  • ブック
  • 無料
https://itunes.apple.com/jp/app/id648195641?mt=8&uo=4&at=11lJEY

‎Susumu HoshikawaのAppをApp Storeで

アプリでは、¥360にてお取り扱いしておりますが、各号ともに 無料お試し版 がございます!まずは無料にて、お試しくださいませ!

f:id:hoshi0523:20150918211845p:plain

以上、最後になりますが、今年もよろしくお願いします!

特攻の島 第8巻が配信開始!

みなさま、いつも当アプリをご利用頂きまして、ありがとうございます。Susumu Hoshikawaでございます。

今回は、拙作アプリ無料で1巻!特攻の島に関するご報告です。

特攻の島

特攻の島

  • Susumu Hoshikawa
  • ブック
  • 無料

最新第8巻、アプリで登場!

佐藤秀峰さんが手がける漫画特攻の島の最新巻第8巻が、iPhoneiPadアプリ無料で1巻!特攻の島にて配信を開始いたしました!

つい先日(11/16)に発売された書籍版に続いて、アプリでも早速取り扱いが開始いたしましたよ!

特攻の島は、特攻兵機「回天」の搭乗員の姿を描いた物語で、当然舞台は太平洋戦争真っ只中。いよいよ物語もクライマックス、最後の作戦に挑む渡辺の運命は!?是非ともご覧下さい!

表紙はこんな感じ!

最新巻の表紙はこんな感じになっています。

これまで全巻、渡辺が表紙を飾っておりますが...前巻までの鬼気迫る表情もなく、何かを悟ったかのような表情になっていますね。物語がどのような展開を迎えるのでしょうか...

f:id:hoshi0523:20151122182516p:plain


価格は¥240!

第2〜7巻と同様、価格は¥240となっております。書籍版と比べると、なんと半額以下です!

場所もとらずに安くあがる、いつでもどこでも読めるiPhone/iPadアプリ版の特攻の島、どうぞお楽しみください!

特攻の島

特攻の島

  • Susumu Hoshikawa
  • ブック
  • 無料

以上、今後とも当アプリをよろしくお願いいたします!

Storyboard Reference がいい感じ

勉強会で持ち帰ったトピックが気になった

先日参加した iOS 9 の勉強会にて、岸川克己さん@Realm が話された Storyboard Reference の話が非常に興味深かったので、自分なりに追加調査してみました。

dev.classmethod.jp

まずはstoryboardの欠点とか

storyboardは、xibでの開発と比べて、画面の遷移が視覚的に分かりやすいのと、segueを利用できるという利点があります。ですが、ファイルが1つなので、チーム開発を行うと競合しやすく、分業しづらいという欠点があります。

そのため、segueを利用できるという利点を破棄してでも画面ごと機能ごとにstoryboardを分割する、という手法が選択されがちだと思います。

分割した別のstoryboardを利用するためには、プログラム側でいろいろやる手間が発生してしまいます。遷移にsegueは利用できず、視覚的にも伝わりづらくなってしまうので、出来れば避けたいですね。

ちなみに、storyboardについてあまりご存知ない方は、この辺りを参考にしてみるといいかもしれません。


ぼっち開発でも困る時は困る

自分のように1人で作業する分には、分業を考える必要はあまりありません。ですが、1つのstoryboardファイルが膨れ上がってしまうのは、やはりあまり嬉しい状況ではないですね。

ちなみに、自分がリリースしている某アプリのstoryboardはこんな感じで、晒すのも恥ずかしいくらい管理が行き届いておりません...

f:id:hoshi0523:20151003190542p:plain

ですが、Xcode 7 からは、この欠点を補うべく、Storyboard Reference という機能が追加されました。


Storyboard Reference とは?

Storyboard Reference は、分割したstoryboardファイル同士を参照し、segueでつなぐことができる機能です。

つまり先ほど挙げた、ファイル分割時の欠点が補われるということですね!

  • 別のstoryboardへの参照もsegueで記述できる
  • 遷移のためのプログラム記述は不要


とりあえず使ってみた

まずは今まで通りに

まずはこんな感じで、画面Aのボタンをタップすると画面BにPUSH遷移を行う、というstoryboardを作ってみます。これまで通りで、特筆すべき点は無いですね。

f:id:hoshi0523:20151003213830p:plain


別のstoryboardファイルを作る

次に、別のstoryboardファイルを生成します(Green.storyboard)。そこに、ViewControllerを1つ配置して、コレを Initial ViewController とします。この画面は画面Cとします。

f:id:hoshi0523:20151003214306p:plain


Storyboard Reference を使う!

画面B上にボタンを配置し、このボタンをタップしたら画面Cに遷移するようにしましょう。

Main.storyboardに、右下のオブジェクト一覧から、storyboard reference をドラッグして配置します(①)。

この Storyboard Reference が Green.storyboard を参照するように設定します(②)。

画面B上のボタンから、この Storyboard Reference に対してPUSH遷移のsegueを設定します(③)。

f:id:hoshi0523:20151003220411p:plain

これで動かしてみれば、画面A画面B画面C の順番で遷移していくと思います。

f:id:hoshi0523:20151003222310g:plain


既存のファイルを分割する場合は?

自分のアプリのように、すでに膨らんでいるstoryboardを分割するのも、編集メニューから簡単に行うことができます。

今回は、Main.storyboard画面B を、別のstoryboardファイルとして分離してみます。


storyboardを分割するよ

分離したいオブジェクトを選択した状態で(①)、Editor → Refactor to Storyborad ... を選択します(②)。

f:id:hoshi0523:20151003223527p:plain


新しくstoryboardが生成された

そうすると、新たにstoryboardが生成されます!(ファイル名は Blue.storyboard としました。)

中には 画面B の ViewController があって、そこから Green.storybard に遷移できるようになっているみたいです。想定通りですね。

f:id:hoshi0523:20151003224155p:plain


元になったファイルも変更されてますね

分割元となった Main.storyboard も、正しく変更されているようです。

見てみてると、画面A の ViewController のみになっており、画面B の代わりにStoryboard Reference が配置されています。これは、先ほど生成された Blue.storyboard を参照しているようです。

f:id:hoshi0523:20151003224803p:plain

この方法を使えば、すでに膨らんでしまったstoryboardファイルを抱えて悩んでいる方も、簡単にファイルを分割できそうですね〜。


iOS 9 のみで使える機能なの?

ちなみに、この Storyboard Reference は、どのバージョンのiOSをサポートしているのでしょうか...?

今回作成したアプリは、Deployment Target を 9.0 にしていましたが、これを 8.0, 7.0 に変更してビルドしてみます。

f:id:hoshi0523:20151004004412p:plain

f:id:hoshi0523:20151004003645p:plain

どうやら、Storyboard Reference は、iOS 8.0 以降の機能っぽいですね。


Relationship Segue も行けちゃう

Navigation ControllerTab Bar Controller のようなコンテナ系の ViewController には、関係性を定義する Relationship Segue というsegueを設定します。これらにも、Storyboard Reference を利用できます。


ベースにあるNavigation Controller

ベースには Navigation Controller を配置しています。Navigation Controller の場合は、Relationship Segue を利用して Root ViewController を設定する必要があります。

今回は Storyboard Reference を利用しているので、Root ViewController には、別のstoryboardファイルに定義された ViewController を設定しています。

f:id:hoshi0523:20151004014317p:plain


Tab Bar Controller が中にいる

Navigation Controller の Root ViewController には、Tab Bar Controller を設定しています。当然、この Tab Bar Controller は、Initial ViewController として設定されています。

Tab Bar Controller にも、Relationship Segue を利用して、各Tabの ViewControllers を設定する必要があります。ここも Storyboard Reference を利用し、別のstoryboardファイルに定義された ViewController を複数設定しています。

f:id:hoshi0523:20151004014649p:plain


各タブごとの ViewController

各タブに利用している ViewController は、一つのstoryboardファイルにまとめて定義しています。

f:id:hoshi0523:20151004015202p:plain

...あれ?Initial ViewController が定義されていなくね?ちゃんと呼ばれるの??と、思う方もいるかもしれません。

Storyboard Reference を利用する場合は、ここまで説明し的してきた「利用するstoryboardファイルを指定して、Initial ViewController に設定されたViewControllerを呼び出す方法」「利用するstoryboardファイルと、利用するViewControllerの Storyboard ID を指定する方法」の2通りあります。


Storyboard ID を使う場合

1つのstoryboardに複数のViewControllerを定義している場合は、後者の方法を採用することで、利用するViewControllerを使い分けることができます。

その場合、各ViewControllerには、それぞれ一意となる Storyboard ID を定義する必要があります。

f:id:hoshi0523:20151004021145p:plain


そして、それらのViewControllerを利用するStoryboard Referenceには、storyboardファイル名とReference ID(Storyboard ID)を設定します。

f:id:hoshi0523:20151004021200p:plain


これを iOS 8 で利用しようとすると...

先ほど調査したように、Storyboard Reference は iOS 8 以降で使えるんだよな〜と思っていると...怒られました

f:id:hoshi0523:20151004022441p:plain

どうやら、Relationship Segue に対して Storyboard Reference を利用する場合は、Deployment Target が 9.0 以降である必要があるみたいですね。

ということで、iOS 8 系をターゲットとするアプリの場合は、relationship segue に対して Storyboard Reference を利用しないようにしましょう!


分割しちゃうと unwind segue って使えないの...?

個人的には、モーダルを閉じる際に unwind segue を使うのですが、ファイルを分割しちゃうと使えなくなっちゃうでしょうか...?ちょっと試したんだけど、やり方がわからなかったです。

設定画面はモーダル表示で、Storyboard Reference を使うぞ! みたいなことって、割と普通にありそうなのですが、こういう場合は unwind segue は諦めろってことなのかな...?

この件に関しては、もう少し調査しないといけないですね(汗)


早く使いたい機能だよ。iOS 7 とか切りましょうよ。

ということで、Storyboard Referenceは、とても魅力的で開発にも良い効果を与えそうな新機能でした。チーム開発を行っている現場では、特に重宝されるんじゃないですかね。

ただ、この機能を導入するには、最低でも iOS 7 のサポートを終了する必要があります。サービスへの影響は小さくないかもしれませんので、サービスの規模によっては、慎重にならざるをえないかもしれませんね...

ちなみに、自分の某アプリのiOSバージョン分布はこんな感じでした。

f:id:hoshi0523:20151004023547p:plain

もう iOS 7 切ってもよくないっすか?

マンガ on ウェブ 第3号が配信開始!

マンガ on ウェブ 第3号

みなさま、いつも当アプリをご利用頂きまして、ありがとうございます。
Susumu Hoshikawaでございます。

先日から各アプリで取り扱いを始めたWeb雑誌 マンガ on ウェブ の 第3号 が、本日から配信開始となります!

f:id:hoshi0523:20150918203751p:plain

第3号はこんな感じ!

本日から配信開始の第3号、表紙はこんな感じ!

f:id:hoshi0523:20151001000641j:plain

新連載を 5本一挙開始 !!
総ページ数は、前号・前々号を上回る 605ページ !!
この表紙のイラストも、 なんかどこかで見たことある !!

THE3名様!!

第3号からは、石原まこちん先生の「THE3名様」が連載を開始します!

f:id:hoshi0523:20151001001310j:plain

ビッグコミックスピリッツ』(小学館)にて2000年より2007年まで連載され、2005年には実写化までされてしまった、ただの日常マンガです!

実写版は、「勇者ヨシヒコ」で有名な福田雄一さんが脚本監督を務めています(笑) そしてキャストにはあの佐藤隆太さん!

お取り扱いはこれらのアプリにて!

第3号は、これらのアプリにて取り扱っております!

海猿 -UMIZARU-

海猿 -UMIZARU-

  • Susumu Hoshikawa
  • ブック
  • 無料
特攻の島

特攻の島

  • Susumu Hoshikawa
  • ブック
  • 無料
https://itunes.apple.com/jp/app/id648195641?mt=8&uo=4&at=11lJEY

‎Susumu HoshikawaのAppをApp Storeで

アプリでは、¥360にてお取り扱いしておりますが、各号ともに 無料お試し版 がございます!まずは無料にて、お試しくださいませ!

f:id:hoshi0523:20150918211845p:plain

以上、今後とも等アプリをよろしくお願いします!

Web漫画雑誌「マンガ on ウェブ」のお取り扱いを始めました

マンガ on ウェブ、始めました

みなさま、いつも当アプリをご利用頂きまして、ありがとうございます。
Susumu Hoshikawaでございます。

今回は、Web雑誌マンガ on ウェブの取り扱いを始めましたことをご報告させていただきます!

マンガ on ウェブの公式?アイコン! f:id:hoshi0523:20150918203751p:plain

まずはこれらのアプリをバージョンアップ!

取り扱っておりますのは、以下の4アプリです。これらを最新版にアップデートしていただければ「マンガ on ウェブ」が表示されます。すでにインストールされている方は、最新バージョンにアップデート後、アプリを起動してみてください。

最新バージョンは3.2.0となっております!!

海猿 -UMIZARU-

海猿 -UMIZARU-

  • Susumu Hoshikawa
  • ブック
  • 無料
特攻の島

特攻の島

  • Susumu Hoshikawa
  • ブック
  • 無料
https://itunes.apple.com/jp/app/id648195641?mt=8&uo=4&at=11lJEY

‎Susumu HoshikawaのAppをApp Storeで


マンガ on ウェブって何ですか?

Web雑誌「マンガ on ウェブ」とは、Webサイト「漫画 on ウェブ」が創刊しているWeb雑誌です。漫画家 佐藤秀峰 さんが制作担当をされていて、今年の4月から4半期ごとに創刊されています。

http://mangaonweb.com/news/2014/11/19/65mangaonweb.com

「漫画 on ウェブ」での取り扱いはもちろん、大小さまざまなマーケットで取り扱われております。この度は、晴れて僕のリリースしているアプリでもお取り扱いさせていただくこととなりました。

現在は創刊号, 第2号を取り扱っておりますが、10/1には第3号も発売されるそうです!アプリでのお取り扱いは10/1には間に合わなさそうなのですが...少々お待ちください
m( _ _ )m

こんな執筆陣!

雑誌に掲載されています執筆陣は、佐藤秀峰さんをはじめ、とっても豪華なメンツになっています。

Stand by me 描クえもん

by 佐藤秀峰

f:id:hoshi0523:20150918205526j:plain

EVIL 〜光と影のタベストリー〜

by 塀内夏子

f:id:hoshi0523:20150918205527j:plain

あいこのまーちゃん

by やまもとありさ

f:id:hoshi0523:20150918205528j:plain

真湖のワイン

by 佐藤智美

f:id:hoshi0523:20150918205529j:plain

などなど...
ほかにも盛りだくさん!

アプリでは、お試し版(無料)も読めるよ

アプリでは、1巻につき¥360にてお取り扱いしておりますが、各巻無料お試し版がございます!まずは無料にて、お試しくださいませ!

f:id:hoshi0523:20150918211845p:plain

以上、今後とも等アプリをよろしくお願いします!

「ハーメルンのバイオリン弾き」の不具合解消のお知らせ

みなさま、いつも当アプリをご利用頂きまして、ありがとうございます。
Susumu Hoshikawaでございます。

先日ご報告させていただきました、アプリ「ハーメルンのバイオリン弾き」の不具合に関しまして、修正版を公開させていただきましたので、ご報告させていただきます。

アップデートをお願いいたします

iPhone / iPadApp Store アプリを開き、右下の アップデート を選択して、「ハーメルンアプリ」のアップデートを行ってください。Mac / PC のiTunesからアップデートを行い、iPhone / iPad と同期を行う、という方法でも構いません。

設定によっては、自動的にアップデート行っている可能性もございます。その場合は、アップデート対象に「ハーメルンアプリ」が表示されていないかもしれません。一度、下記の手順にて現在のバージョンをご確認ください。

アプリのバージョンを確認

上記の手順でアップデートした、もしくはアップデートの対象になっていなかった場合は、アプリを起動し、現在のバージョンを確認してください。現在のバージョンは、トップ画面右上の設定ボタンをタップし、表示された設定画面の一番下で確認できます。

バージョンの値が1.0.1になっていることを確認してください。

f:id:hoshi0523:20150718134939p:plain

大変申し訳御座いません

この度ご迷惑をおかけしたユーザーさまに関しまして、お手数をおかけいたしまして、大変申し訳ございませんでした。深くお詫び申し上げます。

今後とも、当アプリをよろしくお願い致します。