Ios 在UITabBarController内显示带有UITABLEVIEW控制器的ADBannerView

Ios 在UITabBarController内显示带有UITABLEVIEW控制器的ADBannerView,ios,uitableview,iad,Ios,Uitableview,Iad,编辑 感谢@LeoNatan,我现在有了一个完整的工作解决方案。如果有人发现了这个问题并希望得到解决方案,可以在上找到 原始问题 我正试图将IAD(或任何其他视图,尽管它可能特定于ADBannerView)显示在UITabBar上方。我已经尝试了几种不同的方法来实现这一点,但还没有找到满足以下要求的解决方案: 适用于iOS 7和iOS 8 使用和不使用显示的iAd 风景画和肖像画作品 适用于iPhone和iPad UITableViews正确插入更新 到目前为止,我唯一有效的解决方案是将我的

编辑

感谢@LeoNatan,我现在有了一个完整的工作解决方案。如果有人发现了这个问题并希望得到解决方案,可以在上找到

原始问题

我正试图将IAD(或任何其他视图,尽管它可能特定于
ADBannerView
)显示在
UITabBar
上方。我已经尝试了几种不同的方法来实现这一点,但还没有找到满足以下要求的解决方案:

  • 适用于iOS 7和iOS 8
  • 使用和不使用显示的iAd
  • 风景画和肖像画作品
  • 适用于iPhone和iPad
  • UITableView
    s正确插入更新
到目前为止,我唯一有效的解决方案是将我的
UITableView
放在
UIViewController
中,并将
UITableView
ADBannerView
添加到
UIViewController
view
属性中。我离开这里有两个原因:

  • UITableView
    未将其边缘延伸到底部下方
    UITabBar
  • 我需要子类化
    UITableViewController
    ,而不是
    UIViewController
  • 我的
    AppDelegate
    上有一个
    bannerView
    属性和一个
    shouldShowBannerView
    属性来决定是否显示iAd,并共享一个实例。
    AppDelegate
    然后在应该显示或隐藏iAd时(即,当加载iAd时以及当用户付费删除iAd时)发出通知。代码的“基础”是这样工作的:

    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()
                }
            }
        }
    }
    
    迄今为止的问题:

    • 使用
      self.view
      作为超级视图会导致在将-viewdilayoutsubviews发送到视图控制器后仍然需要旋转
      自动布局时崩溃。已收集。AdvertContainerServiceController的实现需要将-LayoutSubView发送到视图以调用自动布局。
    • 我没有正确计算内容插入;当显示iAd时,顶部略微向上跳跃,底部位于横幅顶部下方
    • 表格视图不显示滚动指示器。这似乎是一个已知的问题,但我找不到解决办法
    应Leo Natan的要求,我已经创建了一个新的应用程序,我将根据我的任何尝试进行更新,并在这里解释问题。目前,问题如下:

    第一个选项卡:

    • 显示iAd时,表格顶部向下移动(iOS 8)
    • 无法滚动表格(iOS 7)
    • iAd显示时,表顶视图跳转(iOS 7)
    • 旋转通常会打断iAd的偏移,将其隐藏在选项卡栏后面(iOS 7和iOS 8)
    第二个选项卡:

    • 没有滚动条(iOS 7和iOS 8)
    • 滚动插入未设置(iOS 7)
    • 旋转通常会打断iAd的偏移,将其隐藏在选项卡栏后面(iOS 7和iOS 8)

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

        我的回答被改编成以下GitHub回购协议:

        最好是从苹果网站下载,有tabbar控制器和导航控制器包含示例。
        Apple为您提供了一个抽象视图控制器,它可以自行处理ADBanner流,而不会中断其显示,从而最大限度地延长显示时间

        您可以使用此apple示例,并根据需要对其进行修改。例如,显示或不显示IAD时要注意的bool变量。
        在代码中,您可以看到包含所有逻辑的BannerViewController类。您也可以在那里编写ADmob代码来使用。

        这是我在过去看过的一个尝试过的代码,但无法将内容扩展到选项卡栏下方。你能举一个这样的例子吗?我将表格视图的底部固定到横幅的顶部,我认为这是问题的一部分。如果存在广告视图,您是否仍希望扩展内容?如果是这样,您可能需要自己管理内容和滚动指示器。让表控制器的视图层次结构完全扩展,并根据容器顶部和底部插入更新底部和顶部插入,将ad视图高度添加到底部插入。这是我在原始问题的代码中所做的方法。在
        viewDidLayoutSubviews
        中,我会设置内容插入,它工作正常,但当设备旋转时,在调用
        viewDidLayoutSubviews
        后会计算横幅视图的高度(可能是因为横幅视图不是
        UITableViewController
        的子视图)。不管怎样,我都无法让它手动执行。您应该在容器的视图布局之后设置插入,而不是在表控制器之后。因此,您应该从容器中设置它们。我已经尝试了这一个,并用我的新代码编辑了原始问题,以及一些进一步的问题。您能否评论一下如何可能修复这些问题?谢谢你迄今为止的帮助!:)这是我开始的地方,但不包括自动布局、表视图的插入,并且在iOS 7及以上版本上看起来不正确。如果我做错了什么或者需要修改,请让我知道。事实不是用AL制作的,这不应该是一个问题,自动调整面具的大小(如果r
        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()
                        }
                    }
                }
            }
        
        }