以编程方式在 tableView 和 bottomLayoutGuide 之间添加视图
Programmatically adding a view between a tableView and bottomLayoutGuide
我有一个 UIViewController
和一个 UITableView
填满屏幕的广告支持版本。 MyViewController
嵌入在 UINavigationController
中,底部有一个 UITabBarController
。
此应用程序将有 2 个版本:
1) 付费 - 我在故事板上配置了这个。它按预期工作。
2) 支持广告 - 尽我所能,我无法将横幅画在正确的位置。我正在尝试这样做:
topLayoutGuide
tableview
standard height padding
bannerView (50 height)
standard height padding
bottomLayoutGuide
相反,bannerView
被绘制在 tableView 的顶部,而不是在 tableView
和 bottomLayoutGuide
之间
我从 viewDidLoad
调用了我创建的名为 configureBannerView
的方法。这是用可视格式语言布置视图的代码的相关部分:
var allConstraints = [NSLayoutConstraint]()
let horizontalTableViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[tableView]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalTableViewConstraint
let horizontalBannerViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[leftBannerViewSpacer]-[bannerView(320)]-[rightBannerViewSpacer(==leftBannerViewSpacer)]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalBannerViewConstraint
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide][tableView]-[leftBannerViewSpacer(50)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += verticalConstraints
我不明白为什么这不起作用。下面是完整的 configureBannerView
方法。
func configureBannerView() {
if adSupported == false {
// Do nothing, leave it alone
} else {
// remove existing constraints
tableView.removeConstraints(tableView.constraints)
tableView.translatesAutoresizingMaskIntoConstraints = false
// create dictionary of views
var views: [String : AnyObject] = [
"tableView" : tableView,
"topLayoutGuide": topLayoutGuide,
"bottomLayoutGuide": bottomLayoutGuide]
// Create a frame for the banner
let bannerFrame = CGRect(x: 0, y: 0, width: kGADAdSizeBanner.size.width, height: kGADAdSizeBanner.size.height)
// Instnatiate the banner in the frame you just created
bannerView = GADBannerView.init(frame: bannerFrame)
bannerView?.translatesAutoresizingMaskIntoConstraints = false
// add the bannerView to the view
view.addSubview(bannerView!)
// add bannerView to the view dictionary
views["bannerView"] = bannerView
// Create spacers for left and right sides of bannerView
// 32.0 = leftSpacer left pad + leftSpacer right pad + rightSpacer left pad + rightSpacer right pad
// Calculate width of spacer
let spacerWidth = (screenSize.width - kGADAdSizeBanner.size.width - 32.0) / 2
// Instantiate left and right pads
// 50.0 = height of bannerView
let leftBannerViewSpacer = UIView(frame: CGRect(x: 0, y: 0, width: spacerWidth, height: 50.0))
let rightBannerViewSpacer = UIView(frame: CGRect(x: 0, y: 0, width: spacerWidth, height: 50.0))
leftBannerViewSpacer.translatesAutoresizingMaskIntoConstraints = false
rightBannerViewSpacer.translatesAutoresizingMaskIntoConstraints = false
// add the spacers to the subview
view.addSubview(leftBannerViewSpacer)
view.addSubview(rightBannerViewSpacer)
// add to the views dictionary
views["leftBannerViewSpacer"] = leftBannerViewSpacer
views["rightBannerViewSpacer"] = rightBannerViewSpacer
// Create metric for tabBarHeight
let tabBarHeight = tabBarController?.tabBar.frame.height
// Create a dictionary of metrics
let metrics: [String : CGFloat] = ["tabBarHeight": tabBarHeight!]
var allConstraints = [NSLayoutConstraint]()
let horizontalTableViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[tableView]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalTableViewConstraint
let horizontalBannerViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[leftBannerViewSpacer]-[bannerView(320)]-[rightBannerViewSpacer(==leftBannerViewSpacer)]|",
options: [NSLayoutFormatOptions.AlignAllCenterY],
metrics: nil,
views: views)
allConstraints += horizontalBannerViewConstraint
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide][tableView]-[leftBannerViewSpacer(50)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += verticalConstraints
NSLayoutConstraint.activateConstraints(allConstraints)
感谢您的阅读。我欢迎提出解决我的错误代码的建议。
故事板解决方案
除非所有其他途径都失败了,否则不要以编程方式添加约束:在构建之前无法看到您在做什么,link,运行。
需要更少代码的更简单的解决方案是保留对视图的引用和 Interface Builder 的约束,并且:
constraintToTweak.constant = newValue
Unlike the other properties, the constant may be modified after constraint creation. Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
或
constraintToTweak.active = false
The receiver may be activated or deactivated by manipulating this property. Only active constraints affect the calculated layout. Attempting to activate a constraint whose items have no common ancestor will cause an exception to be thrown. Defaults to NO for newly created constraints.
在 ishaq 的 和 SwiftArchitect 的 的帮助下,我弄明白了。事实证明,让 GADBannerView
在 UITableView
下方正确显示而不将其添加为页脚的关键非常简单。通过执行以下操作,我最终砍掉了 100 行不必要的代码:
1) UIStackView
:如果你以前没有使用过这个,请停止你现在正在做的事情并按照this tutorial.
我将我的 tableView: UITableView
和界面生成器中的 bannerView: GADBannerView
添加到垂直 UIStackView
2) 我在 MyViewController
.
上为他们两个创建了 IBOutlets
(我已经有 tableView
)
3) 我重构后的 configureBannerView
看起来像这样。
// I added these properties at the top. I did not know you could drag
// constraints from Interface Builder onto the ViewController
@IBOutlet weak var bannerViewHeight: NSLayoutConstraint!
@IBOutlet weak var bannerViewWidth: NSLayoutConstraint!
func configureBannerView() {
if adSupported == true {
loadAd() // follow Google's documentation to configure your requests
} else {
bannerView.hidden = true // this hides the bannerView if it's not needed
// Removes constraint errors when device is rotated
bannerViewWidth.constant = 0.0
bannerViewHeight.constant = 0.0
}
}
感谢 ishaq & SwiftArchitect 为我指明了正确的方向。
添加新的 adMob UIViewController 作为根控制器并添加旧根控制器的子视图。
我有一个非常相似的程序问题案例,必须将 adMob 添加到 UITabBarController 根控制器,其中选项卡控制器是 UINavigationController。他们都强烈反对尝试在内部调整它们的大小,广告只是在应用程序视图上输入。 我可能只是不够幸运,找到了一条可行的道路。阅读大量 Whosebug 和 Google 提示。
我也相信 Apples 和 Google 的推荐就像有 下面的广告,与应用程序 有点分离但又很紧密。 Android Admob 横幅以相同的方式出现,并且需要相同的行为。 (最近为 Android 做了同样的项目)。
使用 Admob
创建一个新的应用程序根控制器,一个 ViewController
为了在以后的项目中可以重复使用和顺利实施,大约需要输入一个新的adUnitID(以及旧的根控制器的名称)。横幅将位于应用程序屏幕下方。
只需将这个 class 文件添加到项目中,并使其成为 AppDelegate.swift 文件中的根控制器。
import UIKit
import GoogleMobileAds
class AdsViewController: UIViewController, GADBannerViewDelegate {
let adsTest = true
let ads = true //false //
let adUnitIDTest = "ca-app-pub-3940256099942544/2934735716"
let adUnitID = "ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx"
let tabVc = MainTabBarController()
var bannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
// Add the adMob banner
addBanner(viewMaster: view)
// Add the old root controller as a sub-view
view.addSubview(tabVc.view)
// Make its constraints to fit the adMob
if(ads) {
tabVc.view.translatesAutoresizingMaskIntoConstraints = false
tabVc.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tabVc.view.bottomAnchor.constraint(equalTo: bannerView.topAnchor).isActive = true
tabVc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tabVc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// To be notified of rotation
NotificationCenter.default.addObserver(self, selector: #selector(AdsViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}
}
/*******************************
Ads banner standard engine
*******************************/
func addBanner(viewMaster: UIView) {
if(ads) {
// We instantiate the banner with desired ad size.
bannerView = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)
addBannerViewToView(bannerView, viewMaster: viewMaster)
if(adsTest) {
bannerView.adUnitID = adUnitIDTest
} else {
bannerView.adUnitID = adUnitID
}
bannerView.rootViewController = self
bannerView.load(GADRequest())
bannerView.delegate = self
}
}
func addBannerViewToView(_ bannerView: GADBannerView, viewMaster: UIView) {
bannerView.translatesAutoresizingMaskIntoConstraints = false
viewMaster.addSubview(bannerView)
viewMaster.addConstraints(
[NSLayoutConstraint(item: bannerView,
attribute: .bottom,
relatedBy: .equal,
toItem: viewMaster.safeAreaLayoutGuide,
attribute: .bottom,
multiplier: 1,
constant: 0),
NSLayoutConstraint(item: bannerView,
attribute: .centerX,
relatedBy: .equal,
toItem: viewMaster,
attribute: .centerX,
multiplier: 1,
constant: 0)
])
}
/*******************************
Rotation (change SmartBanner)
*******************************/
@objc func rotated() {
if UIDevice.current.orientation.isPortrait {
bannerView.adSize = kGADAdSizeSmartBannerPortrait
}
if UIDevice.current.orientation.isLandscape {
bannerView.adSize = kGADAdSizeSmartBannerLandscape
}
}
}
我有一个 UIViewController
和一个 UITableView
填满屏幕的广告支持版本。 MyViewController
嵌入在 UINavigationController
中,底部有一个 UITabBarController
。
此应用程序将有 2 个版本:
1) 付费 - 我在故事板上配置了这个。它按预期工作。
2) 支持广告 - 尽我所能,我无法将横幅画在正确的位置。我正在尝试这样做:
topLayoutGuide
tableview
standard height padding
bannerView (50 height)
standard height padding
bottomLayoutGuide
相反,bannerView
被绘制在 tableView 的顶部,而不是在 tableView
和 bottomLayoutGuide
我从 viewDidLoad
调用了我创建的名为 configureBannerView
的方法。这是用可视格式语言布置视图的代码的相关部分:
var allConstraints = [NSLayoutConstraint]()
let horizontalTableViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[tableView]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalTableViewConstraint
let horizontalBannerViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[leftBannerViewSpacer]-[bannerView(320)]-[rightBannerViewSpacer(==leftBannerViewSpacer)]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalBannerViewConstraint
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide][tableView]-[leftBannerViewSpacer(50)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += verticalConstraints
我不明白为什么这不起作用。下面是完整的 configureBannerView
方法。
func configureBannerView() {
if adSupported == false {
// Do nothing, leave it alone
} else {
// remove existing constraints
tableView.removeConstraints(tableView.constraints)
tableView.translatesAutoresizingMaskIntoConstraints = false
// create dictionary of views
var views: [String : AnyObject] = [
"tableView" : tableView,
"topLayoutGuide": topLayoutGuide,
"bottomLayoutGuide": bottomLayoutGuide]
// Create a frame for the banner
let bannerFrame = CGRect(x: 0, y: 0, width: kGADAdSizeBanner.size.width, height: kGADAdSizeBanner.size.height)
// Instnatiate the banner in the frame you just created
bannerView = GADBannerView.init(frame: bannerFrame)
bannerView?.translatesAutoresizingMaskIntoConstraints = false
// add the bannerView to the view
view.addSubview(bannerView!)
// add bannerView to the view dictionary
views["bannerView"] = bannerView
// Create spacers for left and right sides of bannerView
// 32.0 = leftSpacer left pad + leftSpacer right pad + rightSpacer left pad + rightSpacer right pad
// Calculate width of spacer
let spacerWidth = (screenSize.width - kGADAdSizeBanner.size.width - 32.0) / 2
// Instantiate left and right pads
// 50.0 = height of bannerView
let leftBannerViewSpacer = UIView(frame: CGRect(x: 0, y: 0, width: spacerWidth, height: 50.0))
let rightBannerViewSpacer = UIView(frame: CGRect(x: 0, y: 0, width: spacerWidth, height: 50.0))
leftBannerViewSpacer.translatesAutoresizingMaskIntoConstraints = false
rightBannerViewSpacer.translatesAutoresizingMaskIntoConstraints = false
// add the spacers to the subview
view.addSubview(leftBannerViewSpacer)
view.addSubview(rightBannerViewSpacer)
// add to the views dictionary
views["leftBannerViewSpacer"] = leftBannerViewSpacer
views["rightBannerViewSpacer"] = rightBannerViewSpacer
// Create metric for tabBarHeight
let tabBarHeight = tabBarController?.tabBar.frame.height
// Create a dictionary of metrics
let metrics: [String : CGFloat] = ["tabBarHeight": tabBarHeight!]
var allConstraints = [NSLayoutConstraint]()
let horizontalTableViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[tableView]|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: views)
allConstraints += horizontalTableViewConstraint
let horizontalBannerViewConstraint = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[leftBannerViewSpacer]-[bannerView(320)]-[rightBannerViewSpacer(==leftBannerViewSpacer)]|",
options: [NSLayoutFormatOptions.AlignAllCenterY],
metrics: nil,
views: views)
allConstraints += horizontalBannerViewConstraint
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide][tableView]-[leftBannerViewSpacer(50)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += verticalConstraints
NSLayoutConstraint.activateConstraints(allConstraints)
感谢您的阅读。我欢迎提出解决我的错误代码的建议。
故事板解决方案
除非所有其他途径都失败了,否则不要以编程方式添加约束:在构建之前无法看到您在做什么,link,运行。
需要更少代码的更简单的解决方案是保留对视图的引用和 Interface Builder 的约束,并且:
constraintToTweak.constant = newValue
Unlike the other properties, the constant may be modified after constraint creation. Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
或
constraintToTweak.active = false
The receiver may be activated or deactivated by manipulating this property. Only active constraints affect the calculated layout. Attempting to activate a constraint whose items have no common ancestor will cause an exception to be thrown. Defaults to NO for newly created constraints.
在 ishaq 的 和 SwiftArchitect 的 的帮助下,我弄明白了。事实证明,让 GADBannerView
在 UITableView
下方正确显示而不将其添加为页脚的关键非常简单。通过执行以下操作,我最终砍掉了 100 行不必要的代码:
1) UIStackView
:如果你以前没有使用过这个,请停止你现在正在做的事情并按照this tutorial.
我将我的 tableView: UITableView
和界面生成器中的 bannerView: GADBannerView
添加到垂直 UIStackView
2) 我在 MyViewController
.
IBOutlets
(我已经有 tableView
)
3) 我重构后的 configureBannerView
看起来像这样。
// I added these properties at the top. I did not know you could drag
// constraints from Interface Builder onto the ViewController
@IBOutlet weak var bannerViewHeight: NSLayoutConstraint!
@IBOutlet weak var bannerViewWidth: NSLayoutConstraint!
func configureBannerView() {
if adSupported == true {
loadAd() // follow Google's documentation to configure your requests
} else {
bannerView.hidden = true // this hides the bannerView if it's not needed
// Removes constraint errors when device is rotated
bannerViewWidth.constant = 0.0
bannerViewHeight.constant = 0.0
}
}
感谢 ishaq & SwiftArchitect 为我指明了正确的方向。
添加新的 adMob UIViewController 作为根控制器并添加旧根控制器的子视图。
我有一个非常相似的程序问题案例,必须将 adMob 添加到 UITabBarController 根控制器,其中选项卡控制器是 UINavigationController。他们都强烈反对尝试在内部调整它们的大小,广告只是在应用程序视图上输入。 我可能只是不够幸运,找到了一条可行的道路。阅读大量 Whosebug 和 Google 提示。
我也相信 Apples 和 Google 的推荐就像有 下面的广告,与应用程序 有点分离但又很紧密。 Android Admob 横幅以相同的方式出现,并且需要相同的行为。 (最近为 Android 做了同样的项目)。
使用 Admob
创建一个新的应用程序根控制器,一个 ViewController为了在以后的项目中可以重复使用和顺利实施,大约需要输入一个新的adUnitID(以及旧的根控制器的名称)。横幅将位于应用程序屏幕下方。
只需将这个 class 文件添加到项目中,并使其成为 AppDelegate.swift 文件中的根控制器。
import UIKit
import GoogleMobileAds
class AdsViewController: UIViewController, GADBannerViewDelegate {
let adsTest = true
let ads = true //false //
let adUnitIDTest = "ca-app-pub-3940256099942544/2934735716"
let adUnitID = "ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx"
let tabVc = MainTabBarController()
var bannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
// Add the adMob banner
addBanner(viewMaster: view)
// Add the old root controller as a sub-view
view.addSubview(tabVc.view)
// Make its constraints to fit the adMob
if(ads) {
tabVc.view.translatesAutoresizingMaskIntoConstraints = false
tabVc.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tabVc.view.bottomAnchor.constraint(equalTo: bannerView.topAnchor).isActive = true
tabVc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tabVc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// To be notified of rotation
NotificationCenter.default.addObserver(self, selector: #selector(AdsViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}
}
/*******************************
Ads banner standard engine
*******************************/
func addBanner(viewMaster: UIView) {
if(ads) {
// We instantiate the banner with desired ad size.
bannerView = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)
addBannerViewToView(bannerView, viewMaster: viewMaster)
if(adsTest) {
bannerView.adUnitID = adUnitIDTest
} else {
bannerView.adUnitID = adUnitID
}
bannerView.rootViewController = self
bannerView.load(GADRequest())
bannerView.delegate = self
}
}
func addBannerViewToView(_ bannerView: GADBannerView, viewMaster: UIView) {
bannerView.translatesAutoresizingMaskIntoConstraints = false
viewMaster.addSubview(bannerView)
viewMaster.addConstraints(
[NSLayoutConstraint(item: bannerView,
attribute: .bottom,
relatedBy: .equal,
toItem: viewMaster.safeAreaLayoutGuide,
attribute: .bottom,
multiplier: 1,
constant: 0),
NSLayoutConstraint(item: bannerView,
attribute: .centerX,
relatedBy: .equal,
toItem: viewMaster,
attribute: .centerX,
multiplier: 1,
constant: 0)
])
}
/*******************************
Rotation (change SmartBanner)
*******************************/
@objc func rotated() {
if UIDevice.current.orientation.isPortrait {
bannerView.adSize = kGADAdSizeSmartBannerPortrait
}
if UIDevice.current.orientation.isLandscape {
bannerView.adSize = kGADAdSizeSmartBannerLandscape
}
}
}