简体   繁体   English

在UITabBarController内部使用UITableViewController显示ADBannerView

[英]Display ADBannerView with UITableViewController inside UITabBarController

EDIT 编辑

Thanks to @LeoNatan I have now got a complete working solution. 感谢@LeoNatan,我现在有了一个完整的工作解决方案。 If anyone finds this and would like the solution, it's available on GitHub . 如果有人找到它并想​​要解决方案,则可以在GitHub找到它

Original Question 原始问题

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 . 我正在尝试将iAds(或其他任何视图,尽管它可能特定于ADBannerView )显示在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 适用于iOS 7和8
  • Works with and without the iAd displayed 在显示和不显示iAd的情况下均可使用
  • Works in landscape and portrait 适用于风景和肖像
  • Works on iPhone and iPad 适用于iPhone和iPad
  • UITableView s insets correctly update UITableView的插图正确更新

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 . 到目前为止,唯一有效的解决方案是将UITableView放在UIViewController ,并将UITableViewADBannerView添加到UIViewControllerview属性中。 I moved away from this for 2 reasons: 我出于以下两个原因而放弃了这一点:

  1. The UITableView did not extend its edges below the bottom UITabBar UITableView的边缘未在底部UITabBar下方延伸
  2. I need to subclass UITableViewController , not UIViewController 我需要UITableViewController而不是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. 我的AppDelegate上有bannerView属性,而shouldShowBannerView属性用于决定是否显示iAd,并共享一个实例。 The AppDelegate then sends out notifications when iAds should be displayed or hidden (ie, when an iAd is loaded and when the user has paid to remove the iAds). 然后, AppDelegate会在应显示或隐藏iAd时(即,何时加载iAd以及用户为移除iAd付费时)发出通知。 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. 然后,我检查我的viewWillAppear:viewDidDisappear:方法是否要显示showiAds(false)并根据需要调用showiAds(false)hideiAds()

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 UITableViewController s that the iAd was shown/hidden. UITabBarController添加iAd,然后通知UITableViewController s iAd已显示/隐藏。 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. 修改内容/滚动指示器的插入效果不佳,并且UITableViewController经常将其重置为适合导航/选项卡栏的上方/下方。
  • (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? (如上所述)设置content / scroll指标会使我自己插入,但是如果不尝试在viewDidLayoutSubviews模拟(使用(top | bottom)LayoutGuide),就无法使其保持一致,但这似乎非常昂贵吗?
  • 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) 我确实做到了,它通过将ADBannerViewUITableViewController内添加到某个视图中而ADBannerView ,但是在iOS 7上它会崩溃(关于tableView的事情必须调用super -layoutSubviews)

EDIT 编辑

I have created a UIViewController subclass with the intent of using it to house UITableViewControllers via a Container View . 我创建了一个UIViewController子类,目的是使用它通过Container View来容纳UITableViewControllers Here is what I have so far, followed by a couple of issues: 这是我到目前为止的内容,然后是几个问题:

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()
                }
            }
        }
    }

}

Issues so far: 到目前为止的问题:

  • Using self.view as the superview causes a crash on rotate 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. 使用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. 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.
  • I'm not calculating the content insets correctly; 我没有正确计算内容插图; when the iAd is shown, the top jumps up slightly and the bottom in below the top of the banner 显示iAd时,顶部略微跳起,底部在横幅顶部下方
  • The table view doesn't show the scroll indicators. 表格视图未显示滚动指示器。 This seems to be a known issue but I cannot find a solution 这似乎是一个已知问题,但我找不到解决方案

At the request of Leo Natan I have create a repo on GitHub that I will update with any attempts I make, and explain issues here. 应Leo Natan的要求,我在GitHub上创建了一个存储 ,我将对其进行任何尝试进行更新,并在此处说明问题。 Currently, the issues are as follows: 当前,问题如下:

First Tab: 第一个标签:

  • Top of table moves down when iAd is shown (iOS 8) 显示iAd时,表格顶部向下移动(iOS 8)
  • Table cannot be scrolled (iOS 7) 表格无法滚动(iOS 7)
  • Top of table view jumps when iAd shows (iOS 7) 当iAd显示时,表格顶部视图会跳转(iOS 7)
  • Rotation often breaks the offset of the iAd, hiding it behind the tab bar (iOS 7 and 8) 旋转通常会中断iAd的偏移,将其隐藏在标签栏的后面(iOS 7和8)

Second Tab: 第二个标签:

  • There are no scroll bars (iOS 7 and 8) 没有滚动条(iOS 7和8)
  • Scroll inset it not set (iOS 7) 滚动插入未设置(iOS 7)
  • Rotation often breaks the offset of the iAd, hiding it behind the tab bar (iOS 7 and 8) 旋转通常会中断iAd的偏移,将其隐藏在标签栏的后面(iOS 7和8)

The best solution is to use view controller containment. 最好的解决方案是使用视图控制器遏制。 Use a view controller subclass that will house both the ad view and the table view controller's view, and add the table view controller as a child of the container view controller. 使用将包含广告视图和表视图控制器的视图的视图控制器子类,并将表视图控制器添加为容器视图控制器的子级。 This should take care of content insets correctly. 这应该正确处理内容插图。 On each layout of the container controller's view, position the table controller view hierarchy correctly after positioning the ad view. 在容器控制器视图的每个布局上,在放置广告视图之后,正确定位表控制器视图层次结构。 If you wish to hide the ad view, simply hide or remove it from the container hierarchy, and extend the table controller's view hierarchy fully. 如果要隐藏广告视图,只需将其隐藏或从容器层次结构中删除,然后完全扩展表控制器的视图层次结构。 When working with hierarchies, remember to always use the table controller's view and not the tableView directly. 使用层次结构时,请记住始终使用表控制器的view而不是直接使用tableView

My answer was adapted into the following GitHub repo: https://github.com/JosephDuffy/iAdContainer 我的答案已改编为以下GitHub存储库: https : //github.com/JosephDuffy/iAdContainer

The best that is that you download the AD suite from Apple site, there are tabbar controller and navigation controller containment example. 最好的办法是从Apple网站下载AD套件 ,其中有标签栏控制器和导航控制器包含示例。
Apple provides you an abstract view controller that can handle by itself the ADBanner flow without interrupting its presentation, maximizing the showing time. Apple为您提供了一个抽象视图控制器,该控制器可以自己处理ADBanner流,而不会中断其显示,从而最大限度地延长了显示时间。

You can use this https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html apple sample and modified it according to your needs. 您可以使用此https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html苹果示例并根据需要对其进行修改。 Such as bool variable to take care of when iAds is shown or not. 例如bool变量,可用于显示或不显示iAds。 There in code you can see BannerViewController class that contains all the logic. 在代码中,您可以看到包含所有逻辑的BannerViewController类。 You can also write ADmob code there to use. 您也可以在此处编写ADmob代码以供使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM