在UITabBarController中使用UITableViewController显示ADBannerView

Jos*_*ffy 2 uitableview ios iad

编辑

感谢@LeoNatan,我现在有了一个完整的工作解决方案.如果有人发现这个并想要解决方案,它可以在GitHub找到.

原始问题

我正在尝试将iAds(或任何其他视图,尽管它可能是特定的ADBannerView)显示在a上方UITabBar.我已经采用了几种不同的方法来做到这一点,但还没有提出满足以下条件的解决方案:

  • 适用于iOS 7和8
  • 无论是否显示iAd都可以使用
  • 在景观和肖像中工作
  • 适用于iPhone和iPad
  • UITableViews insets正确更新

我迄今已工作的唯一解决方案一直有我的UITableView里面UIViewController,并添加UITableViewADBannerViewview的财产UIViewController.我离开了这个有两个原因:

  1. UITableView没有底部下方延伸的边缘UITabBar
  2. 我需要子类UITableViewController,而不是UIViewController

bannerView在我AppDelegate和一个属性上有一个属性shouldShowBannerView来决定是否显示iAd,并共享一个实例.所述AppDelegate随后发送出去时成瘾者应显示或隐藏的通知(即,当IAD设备被加载,并在用户已支付以除去IADS).代码的"基础"如下:

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()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我检查我viewWillAppear:viewDidDisappear:方法是否应该显示iAds showiAds(false)hideiAds()根据需要调用.

无论我做什么,我似乎都无法让它发挥作用.我尝试过的其他一些事情却废弃了以下代码:

  • 添加iAd UITabBarController,然后警告UITableViewControllers显示/隐藏了iAd.修改内容/滚动指示器插入不能很好地工作,并通过UITableViewController适合导航/标签栏上方/下方的重置.
  • (如上所述)设置内容/滚动指示器自我插入,但我无法在不尝试模拟(使用(顶部|底部)布局指南)的情况下保持一致viewDidLayoutSubviews,但这看起来非常昂贵?
  • 我曾经做过,通过添加ADBannerView到内部的一些视图来使它工作UITableViewController,但它会在iOS 7上崩溃(关于tableView的一些事情必须调用super -layoutSubviews)

编辑

我创建了一个UIViewController子类,用它容纳的意图UITableViewControllers通过Container View.这是我到目前为止所做的,接下来是几个问题:

class AdvertContainerViewController: UIViewController {
    var tableViewController: UITableViewController?
    var showingiAd = false
    var bannerBottomConstraint: NSLayoutConstraint?
    private var bannerTopOffset: CGFloat {
        get {
            var offset: CGFloat = 0
            if let tabBar = self.tabBarController?.tabBar {
                offset -= CGRectGetHeight(tabBar.frame)
            }

            if let bannerView = AppDelegate.instance.bannerView {
                let bannerViewHeight = bannerView.frame.size.height
                offset -= bannerViewHeight
            }

            return offset
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        if self.childViewControllers.count > 0 {
            if let tableViewController = self.childViewControllers[0] as? UITableViewController {
                self.tableViewController = tableViewController
                tableViewController.automaticallyAdjustsScrollViewInsets = false
                self.navigationItem.title = tableViewController.navigationItem.title
            }
        }
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        if AppDelegate.instance.shouldShowBannerView {
            self.showiAds(false)
        }
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        let delegate = AppDelegate.instance
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "showiAds", name: "BannerViewDidLoadAd", object: delegate)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideiAds", name: "RemoveBannerAds", object: delegate)
    }

    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)

        if self.showingiAd {
            self.hideiAds()
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        println("View did layout subviews")

        if self.showingiAd {
            if let bannerView = AppDelegate.instance.bannerView {
                let bannerViewHeight = CGRectGetHeight(bannerView.frame)

                if let bottomConstraint = self.bannerBottomConstraint {
                    let bannerTopOffset = self.bottomLayoutGuide.length + bannerViewHeight
                    if bottomConstraint.constant != bannerTopOffset {
                        println("Setting banner top offset to \(bannerTopOffset)")
                        bottomConstraint.constant = -bannerTopOffset
                        bannerView.superview?.setNeedsUpdateConstraints()
                        bannerView.superview?.updateConstraintsIfNeeded()
                    }
                }

                println("Bottom layout guide is \(self.bottomLayoutGuide.length)")
                let insets = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length + bannerViewHeight, 0)
                self.updateTableViewInsetsIfRequired(insets)

            }
        }
    }

    private func updateTableViewInsetsIfRequired(insets: UIEdgeInsets) {
        if let tableView = self.tableViewController?.tableView {
            if !UIEdgeInsetsEqualToEdgeInsets(tableView.contentInset, insets) {
                println("Updating content insets to \(insets.top), \(insets.bottom)")
                tableView.contentInset = insets
            }
            if !UIEdgeInsetsEqualToEdgeInsets(tableView.scrollIndicatorInsets, insets) {
                println("Updating scroll insets to \(insets.top), \(insets.bottom)")
                tableView.scrollIndicatorInsets = insets
            }
        }
    }

    func showiAds() {
        self.showiAds(true)
        //        self.showiAds(false)
    }

    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!

                // 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 bannerBottomConstraint = NSLayoutConstraint(item: bannerView, attribute: .Top, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Bottom, multiplier: 1, constant: 0)
                self.bannerBottomConstraint = bannerBottomConstraint
                bannersSuperview.addConstraint(bannerBottomConstraint)

                bannersSuperview.layoutSubviews()
                bannersSuperview.layoutIfNeeded()

                let topInset = self.navigationController?.navigationBar.frame.size.height ?? 0
                let insets = UIEdgeInsetsMake(topInset, 0, -self.bannerTopOffset, 0)

                // 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 = self.bannerTopOffset
                bannersSuperview.setNeedsUpdateConstraints()
                UIView.animateWithDuration(animated ? 0.5 : 0, animations: { () -> Void in
                    // Calling layoutIfNeeded here will animate the layout constraint cosntant change made above
                    self.updateTableViewInsetsIfRequired(insets)
                    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()
                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

迄今为止的问题:

  • 使用self.view超级视图会导致旋转崩溃Auto Layout still required after sending -viewDidLayoutSubviews to the view controller. Gathered.AdvertContainerViewController's implementation needs to send -layoutSubviews to the view to invoke auto layout.
  • 我没有正确计算内容插入; 当显示iAd时,顶部略微向上跳跃,底部在横幅顶部下方
  • 表视图不显示滚动指示符.这似乎是一个已知问题,但我找不到解决方案

应Leo Natan的要求,我已经在GitHub上创建了一个回购,我将根据我的任何尝试进行更新,并在此处解释问题.目前,问题如下:

第一个标签:

  • 当显示iAd时,表顶部向下移动(iOS 8)
  • 表无法滚动(iOS 7)
  • 当iAd显示时,表顶视图跳转(iOS 7)
  • 旋转经常打破iAd的偏移,将其隐藏在标签栏后面(iOS 7和8)

第二个标签:

  • 没有滚动条(iOS 7和8)
  • 滚动插入未设置(iOS 7)
  • 旋转经常打破iAd的偏移,将其隐藏在标签栏后面(iOS 7和8)

Leo*_*ica 5

最好的解决方案是使用视图控制器包含.使用视图控制器子类,它将包含广告视图和表视图控制器的视图,并将表视图控制器添加为容器视图控制器的子视图.这应该正确地处理内容插入.在容器控制器视图的每个布局上,在定位广告视图后正确定位表控制器视图层次结构.如果要隐藏广告视图,只需在容器层次结构中隐藏或删除它,并完全扩展表控制器的视图层次结构.使用层次结构时,请记住始终使用表控制器view而不是tableView直接使用表控制器.

我的答案被改编成以下GitHub回购:https: //github.com/JosephDuffy/iAdContainer