I'm trying to get iAds (or any other view for that matter, although it may be specific to ADBannerView) to be displayed just above a UITabBar. I've gone about a few different ways of doing this, but haven't come up with a solution that satifies the following:

  • Works on iOS 7 and 8
  • Works with and without the iAd displayed
  • Works in landscape and portrait
  • Works on iPhone and iPad
  • UITableViews insets correctly update

The only solution I have so far that has worked has been to have my UITableView inside a UIViewController, and adding the UITableView and ADBannerView to the view property of the UIViewController. I moved away from this for 2 reasons:

  1. The UITableView did not extend its edges below the bottom UITabBar
  2. I need to subclass UITableViewController, not UIViewController

I have a bannerView property on my AppDelegate and a shouldShowBannerView property to decide whether or not to show the iAd, and share a single instance. The AppDelegate then sends out notifications when iAds should be displayed or hidden (i.e., when an iAd is loaded and when the user has paid to remove the iAds). The "base" of the code works as such:

func showiAds(animated: Bool) {
    if !self.showingiAd {
        let delegate = UIApplication.sharedApplication().delegate as AppDelegate
        if let bannerView = delegate.bannerView {
            println("Showing iAd")
            self.showingiAd = true

            if (bannerView.superview != self.view) {
                bannerView.removeFromSuperview()
            }

//                let bannersSuperview = self.view.superview! // Bottom inset incorrect
            let bannersSuperview = self.view // Banner is shown at the top screen. Crashes on iOS 7 (at bannersSuperview.layoutIfNeeded())
//                let bannersSuperview = self.tableView // The is the same as self.view (duh)
//                let bannersSuperview = self.tabBarController!.view // Bottom inset incorrect

            // Added the view and the left/right constraints allow for the proper height
            // to be returned when bannerView.frame.size.height is called (iOS 7 fix mainly)
            bannersSuperview.addSubview(bannerView)
            bannersSuperview.addConstraints([
                NSLayoutConstraint(item: bannerView, attribute: .Left, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Left, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: bannerView, attribute: .Right, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Right, multiplier: 1, constant: 0),
                ])
            bannersSuperview.layoutIfNeeded()

            let bannerViewHeight = bannerView.frame.size.height

            var offset: CGFloat = -self.bottomLayoutGuide.length
            if (UIDevice.currentDevice().systemVersion as NSString).floatValue < 8 {
                // Seems to be needed for some reason
                offset -= bannerViewHeight
            }
            let bannerBottomConstraint = NSLayoutConstraint(item: bannerView, attribute: .Bottom, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Bottom, multiplier: 1, constant: offset + bannerViewHeight)
//                self.bannerBottomConstraint = bannerBottomConstraint
            bannersSuperview.addConstraint(bannerBottomConstraint)

            bannersSuperview.layoutSubviews()
//               bannerSuperview.setNeedsLayout()
            bannersSuperview.layoutIfNeeded()

            // Previously, this values was the height of the banner view, so that it starts off screen.
            // Setting this to 0 and then doing an animation makes it slide in from below
            bannerBottomConstraint.constant = offset
            bannersSuperview.setNeedsUpdateConstraints()
            UIView.animateWithDuration(animated ? 10 : 0, animations: { () -> Void in
                // Calling layoutIfNeeded here will animate the layout constraint cosntant change made above
                bannersSuperview.layoutIfNeeded()
            })
        } else {
            println("Cannot show iAd when bannerView is nil")
        }
    }
}

func hideiAds() {
    if self.showingiAd {
        self.showingiAd = false
        let delegate = UIApplication.sharedApplication().delegate as AppDelegate
        if let bannerView = delegate.bannerView {
            if bannerView.superview == self.view {
                bannerView.removeFromSuperview()
            }
        }
    }
}

I then check in my viewWillAppear: and viewDidDisappear: methods if an iAds is/should be displayed and calling showiAds(false) and hideiAds() as required.

No matter what I do, I don't seem to be able to get it to work. A couple of other things I've tried but scrapped the code for:

  • Adding the iAd in the UITabBarController, which then alerts the UITableViewControllers that the iAd was shown/hidden. Modifying the content/scroll indicator insets did not work well, and was ofter reset by the UITableViewController to fit above/below the navigation/tab bar.
  • (as above) setting the content/scroll indicator insets myself, but I could not get it consistent without attempting to emulate (using (top|bottom)LayoutGuide) in viewDidLayoutSubviews, but this seems very costly?
  • I did, at one point, have it working by adding the ADBannerView to some view from within the UITableViewController, but it would crash on iOS 7 (something about tableView must call super -layoutSubviews)

Related posts

Recent Viewed