I have an AdBannerView inside my game, but it keeps showing randomly even though I set it to hidden, it pops from the bottom pushing the view up.
Here's the code I have thus far in GameScene:
var iAd = ADBannerView()
override func didMoveToView(view: SKView) {
iAd.delegate = self
iAd.hidden = true
iAd.autoresizingMask = UIViewAutoresizing.FlexibleTopMargin
view.addSubview(iAd)
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
if (!isStarted){ // <- If game has started
iAd.hidden = false
}
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
print("Ad Fail")
iAd.hidden = true
}
func newGame() {
iAd.hidden = true
}
func gameOver() {
iAd.hidden = false
}
Sometimes the ad shows during gameplay, sometimes it shows at the top, other times at the bottom.
My questions are:
How do I position it at the top?
How do I make it so that it stops appearing during gameplay?
How do I make it stop pushing the view up?
How do I make it stop showing if it failed to load (it currently does show)
MORE INFO: I tried this code in ViewController, but ended up with the same results.
For question 1: You have set the frame property to position the banner ad:
iAd.frame = CGRectMake(0, view.frame.size.height - iAd.frame.size.height, view.frame.size.width, iAd.frame.size.height);
Sounds like you've created an ADBannerView in Interface Builder in addition to including self.canDisplayBannerAds somewhere in your project.
The ADBannerView displaying on the bottom of your devices screen is created by self.canDisplayBannerAds = true. self.canDisplayBannerAds = true can be used for a no hassle way of implementing iAd banners in your application. This will create an ADBannerView for you and show or hide the ADBannerView depending on whether it receives an ad or not from the iAd network.
If you have included self.canDisplayBannerAds = true in your project you need to remove it.
As for hiding the ADBannerView when it fails to receive an advertisement, you need to implement the ADBannerView's delegate methods: ADBannerViewDelegate Protocol Reference. Then, in bannerView(_:didFailToReceiveAdWithError:) you'd set your ADBannerView's alpha property to 0.
Related
I have this app that I am trying to use AdMob with that is made with SwiftUI. I had been using official AdMob documentation with SwiftUI to help me do this. I have one issue however, the ad successfully gets loaded, but the ad is located underneath another view, much like a ZStack without a ZStack present. No matter where I put it, it gets placed beneath a view. I have used the .frame() attribute, and it works using the BannerAd view, but then there is a giant white space there when the ad can't be loaded. Here are screenshots that may help solve the issue:
(Image on the left) This is what happens using the Google provided code (They say that the banner would reload into its own view when the ad is loaded, but does not appear to happen) As you can see, the add is below the name of the current herb.
(Image on the right)This is what happens when using the .frame() attribute before the ad is loaded. The ad loads correctly, but leaves this white space when not loaded or unable to load.
Here is the code to make the banner work with SwiftUI
import SwiftUI
import Foundation
import GoogleMobileAds
protocol BannerViewControllerWidthDelegate: AnyObject {
func bannerViewController(_ bannerViewController: BannerViewController, didUpdate width: CGFloat)
}
class BannerViewController: UIViewController {
weak var delegate: BannerViewControllerWidthDelegate?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Tell the delegate the initial ad width.
delegate?.bannerViewController(self, didUpdate: view.frame.inset(by: view.safeAreaInsets).size.width)
}
override func viewWillTransition(
to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator
) {
coordinator.animate { _ in
// do nothing
} completion: { _ in
// Notify the delegate of ad width changes.
self.delegate?.bannerViewController(
self, didUpdate: self.view.frame.inset(by: self.view.safeAreaInsets).size.width)
}
}
}
struct BannerAd: UIViewControllerRepresentable {
#State private var viewWidth: CGFloat = .zero
private let bannerView = GADBannerView()
private let adUnitID = "ca-app-pub-3940256099942544/6300978111"
// Make the view to be used in SwiftUI
func makeUIViewController(context: Context) -> some UIViewController {
let bannerViewController = BannerViewController()
bannerView.adUnitID = adUnitID
bannerView.rootViewController = bannerViewController
bannerViewController.view.addSubview(bannerView)
bannerViewController.delegate = context.coordinator
return bannerViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
if(viewWidth != .zero){
// Nothing happens
} else {
// Return when the size is greater than 0
//TODO: Fix !!!! This does not push other views down as it should, a frame could be set, but the ad does not push everything down or up
return
}
// Request a banner ad with the updated viewWidth.
bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth)
bannerView.load(GADRequest())
}
}
}
The code may be long, but you can ignore the coordinator as that is only for listeners, not nay real displaying of the advertisement.
What I want to happen exactly is for the Ad to move every object down so that when the ad is loaded, it will fit respectfully at the top of the page. I have looked at all sorts of sources, but I cannot find any solutions to this anywhere. Please help me, and thank you!
I'm adding an adaptive banner ad from AdMob to the bottom of a mapView in my iOS app. No problem adding it, but the banner obscures both a segmentedControl which is placed at the bottom of the mapView. (Both the mapView and the segmentedControl are inside a tabBarController, all created in Storyboard.) The ad also covers the Apple Mags logo and "Legal" information - not sure if Apple will have a problem with that.
Image example - top of segmentedControl barely visible under ad
How can I make the mapView shift up by the same amount of space taken up by the ad, so as to make the segmentedControl and Apple Maps boilerplate visible, and not shift up at all if no ad is loaded? (Or is there another best practice of handling this?)
All my AdMob code is from the official AdMob tutorial:
func getAdaptiveSize() -> GADAdSize {
var frame: CGRect
if #available(iOS 11.0, *) {
frame = view.frame.inset(by: view.safeAreaInsets)
} else {
frame = view.frame
}
let viewWidth = frame.size.width
return adSize
}
func loadAdaptiveBannerAd(){
bannerView.adSize = getAdaptiveSize()
bannerView.load(GADRequest())
}
func addBannerToView(){
bannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
override func viewDidLoad() {
super.viewDidLoad()
bannerView.adUnitID = "ca-app-pub-3940256099942544/2435281174"
bannerView.rootViewController = self
bannerView.backgroundColor = .darkGray
addBannerToView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadAdaptiveBannerAd()
}
My hunch is that I have to fix this not by changing the placement of the ad banner itself, but by approaching either the mapView or the superview somehow, but I haven't found the solution.
You didn't show your actual layout, but I'm going to assume it looks very close to this:
The Bottom of your mapView is constrained to the Bottom of the safe-area, and the Bottom of your Segmented control is constrained to the Bottom of the mapView. If you have your SegControl also constrained to the safe-area, change that to the mapView -- that way we only have to manipulate the mapView and the SegControl will move with it.
First step, change the Priority of the Map view's bottom constraint to High (750):
and your constraints will look like this:
Next, connect that constraint to an #IBOutlet:
#IBOutlet var mapBottomConstraint: NSLayoutConstraint!
result:
Now at run-time, when you add your banner view, add a NEW Bottom constraint to your map view:
func addBannerToView(){
bannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// add this line
mapView.bottomAnchor.constraint(equalTo: bannerView.topAnchor)
])
}
Because the newly created constraint will have the default Priority of Required (1000), auto-layout will "pin" the bottom of the map view to the top of the banner. Since we gave the Storyboard-created Bottom constraint a Priority of 750, we've told auto-layout it's allowed to break that constraint without causing conflicts.
If / when you remove the banner view, that "new" constraint will be removed with it, and the Storyboard-created constraint will again be used.
I'm trying to place an image behind the status bar.
I'm able to turn it transparent but It's still blocking the image from appearing behind it.
Does anybody know how to make the status bar apart of the editable screen and/or safe area? I don't want to delete it, just want to put stuff behind it.
Here's what IB looks like
Code
override func viewWillAppear(_ animated: Bool) {
setNeedsStatusBarAppearanceUpdate()
}
override func viewDidLoad() {
super.viewDidLoad()
venueInfoTableView.dataSource = self
venueInfoTableView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
venueInfoTableView.separatorStyle = .none
}
override var preferredStatusBarStyle : UIStatusBarStyle {
return UIStatusBarStyle.lightContent
//return UIStatusBarStyle.default // Make dark again
}
And here's the result
You should disable automaticallyAdjustsScrollViewInsets
if #available(iOS 11.0, *) {
self.venueInfoTableView.contentInsetAdjustmentBehavior = .never
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
contentInsetAdjustmentBehavior This property specifies how the safe
area insets are used to modify the content area of the scroll view.
The default value of this property is automatic.
I have been struggling with UISearchController's search bar for quite a while now. I need a search feature to be implemented on a tableview but unlike conventional methods, I didn't put the search bar on a table header view. Instead, I created a UIView and added the search bar as a subview to it. The UIView which acts as a container to search bar has its constraints properly set on the storyboard with auto layout.
Here are my codes for it. Note that I did this programmatically because UISearchDisplayController and UISearchBar has been deprecated as of iOS 8 in favour of UISearchController and has yet to come to UIKit.
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.autoresizingMask = .FlexibleRightMargin
searchController.searchBar.delegate = self
definesPresentationContext = true
self.searchContainerView.addSubview(searchController.searchBar)
However, I did notice one odd behaviour of the search bar during rotation. When it is active on Portrait, I rotate the simulator to Landscape, and press Cancel, the search bar goes back to Portrait Width.
The same happens the other way around too.
I would appreciate any ideas or maybe some hints towards the correct direction to solve this as I have been at this for days at least. Thank you very much
So after so many days of struggling with this:
Portrait to Landscape
Make SearchBar/SearchController active in Portrait
Rotate to Landscape
Press Cancel and SearchBar will go back to Portrait width
Landscape to Potrait
Make SearchBar/SearchController active in Landscape
Rotate to Portrait
Press Cancel and SearchBar will go back to Landscape width
I finally solved it on my own. It seems that when a user presses Cancel on the SearchBar, the ViewController will call viewDidLayoutSubviews thus I tried to reset the width by calling this function in viewDidLayoutSubviews:
func setupSearchBarSize(){
self.searchController.searchBar.frame.size.width = self.view.frame.size.width
}
But that didn't work as well as I thought. So here is what I think happens. When a user activates the SearchController/SearchBar, the SearchController saves the current frame before it resizes itself. Then when a user presses Cancel or dismisses it, it will use the saved frame and resize to that frame size.
So in order to force its width to be realigned when I press Cancel, I have to implement the UISearchControllerDelegate in my VC and detect when the SearchController is dismissed, and call setupSearchBarSize() again.
Here are the relevant codes to solve this question:
class HomeMainViewController : UIViewController, UISearchControllerDelegate, UISearchResultsUpdating, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var searchContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.autoresizingMask = .FlexibleRightMargin
searchController.searchBar.delegate = self
searchController.delegate = self
definesPresentationContext = true
self.searchContainerView.addSubview(searchController.searchBar)
setupSearchBarSize()
}
func setupSearchBarSize(){
self.searchController.searchBar.frame.size.width = self.view.frame.size.width
}
func didDismissSearchController(searchController: UISearchController) {
setupSearchBarSize()
}
override func viewDidLayoutSubviews() {
setupSearchBarSize()
}
}
Here is simpler solution:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
self.searchController.searchBar.frame.size.width = self.view.frame.size.width
}, completion: nil)
}
I have the following code.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.canDisplayBannerAds = true
self.theTopAdBannerView?.hidden = false
func loadAds(){
theTopAdBannerView.delegate = self
theTopAdBannerView.hidden = false
view.addSubview(theTopAdBannerView)
}
loadAds()
}
I made sure I linked up theTopAdBannerView outlet to the AdBanner. When I open it, it just shows a blank white space on where I placed the AdBanner in the storyboard. Why aren't my ads showing up?
I get this error
Error Domain=ADErrorDomain Code=5 "The operation couldn’t be completed. Banner view is visible but does not have content" UserInfo=0x7f9e2b5e3640 {ADInternalErrorCode=5, NSLocalizedFailureReason=Banner view is visible but does not have content, ADInternalErrorDomain=ADErrorDomain}
Below code worked for me, Hope it works for you as well.
func displayAd(){
adBannerView = ADBannerView(frame: CGRect.zeroRect)
adBannerView.center = CGPoint(x: adBannerView.center.x, y: view.bounds.size.height - adBannerView.frame.size.height / 2)
adBannerView.delegate = self
adBannerView.hidden = true
view.addSubview(adBannerView)
}
Calling loadAds from viewDidLoad method.
In didLoadAd method made adBannerView.hidden = false
AND
In didFailToReceiveAdWithError made adBannerView.hidden = true
Apparently my ad wasn't loading. All of a sudden it loads fine now. The code is fine.