AutoLayout を使って ADBannerView を配置する方法

ADBannerView は広告取得に失敗したら隠さないと、レビューでリジェクトされてしまう。

今までは広告取得に失敗したら frame を操作して画面外に移動していた。そのためのモジュールを Objective-C で書いたりもした。でも最近は AutoLayout を使うようにしている。

RubyMotion のサンプルは次の通り。

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    @window.rootViewController = MainViewController.controllerWithNavigation
    @window.makeKeyAndVisible
    true
  end
end

class MainViewController < UIViewController
  CELL_ID = "Cell"

  def self.controllerWithNavigation
    UINavigationController.alloc.initWithRootViewController(
      self.alloc.initWithNibName(nil, bundle:nil)
    )
  end

  def viewDidLoad
    super
    self.title = "MotionLaoutSample"

    @tableView = UITableView.alloc.initWithFrame(
      CGRectZero,
      style:UITableViewStylePlain
    ).tap do |table|
      table.delegate = table.dataSource = self
      self.view.addSubview(table)
    end

    @bannerView = ADBannerView.alloc.initWithFrame(CGRectZero).tap do |banner|
      banner.delegate = self
      banner.hidden = true
      self.view.addSubview(banner)
    end

    # motion-layout をつかってレイアウト
    Motion::Layout.new do |layout|
      layout.view self.view
      layout.subviews "table" => @tableView, "banner" => @bannerView
      layout.vertical "|-0-[table]-0-[banner]-0-|"
      layout.horizontal "|-0-[table]-0-|"
      layout.horizontal "|-0-[banner]-0-|"
    end

    # ADBannerView の高さを動的に変更したいので
    # 制約をメンバに保持しておく
    @bannerHeight = NSLayoutConstraint.constraintWithItem(
      @bannerView,
      attribute: NSLayoutAttributeHeight,
      relatedBy: NSLayoutRelationEqual,
      toItem: nil,
      attribute: NSLayoutAttributeHeight,
      multiplier: 1,
      constant: 0 
    )
    @bannerView.addConstraint(@bannerHeight)
  end

  def tableView(tableView, numberOfRowsInSection:section)
    20
  end

  def tableView(tableView, cellForRowAtIndexPath:indexPath)
    cell = tableView.dequeueReusableCellWithIdentifier(CELL_ID)
    unless cell
      cell = UITableViewCell.alloc.initWithStyle(
        UITableViewCellStyleDefault,
        reuseIdentifier: CELL_ID
      )
    end
    cell.textLabel.text = "Row#{indexPath.row}"
    cell
  end

  def bannerView(banner, didFailToReceiveAdWithError:error)
    # 広告の取得に失敗したら banner を隠す
    banner.hidden = true
    @bannerHeight.constant = 0
    self.view.layoutIfNeeded
  end

  def bannerViewDidLoadAd(banner)
    # 広告の取得に成功したら banner を表示する
    @bannerHeight.constant = 50
    banner.hidden = false
    self.view.layoutIfNeeded
  end
end

RubyMotion での AutoLayout には motion-layout が便利。ADBannerView の高さの制約を動的に変更したかったので、そこだけは NSLayoutConstraint を直接使っている。

frame を変更する方法の場合、ADBannerView と UITableView 両方変更しないといけなかった。AutoLayout なら高さの制約を変更するだけで、あとはよしなにやってくれる。 AutoLayout ホント便利。