How do I call an interstitial ad to load on another UIViewController? - swift

I have a UIViewController with a UIView displayed on it; pressing a button on the UIView loads my interstitial ad. When the UIView is subsequently displayed, I want the interstitial to be displayed with the rootVC as the UIViewController.
However, this code does not seem to work as intended:
1) My View Controller:
class MyViewController: UIViewController {
let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("BUTTON", for: .normal)
btn.addTarget(self, action: #selector(showUIView), for: .touchUpInside)
return btn
}()
#objc func showUIView(_ sender: UIButton) {
let popUp = MyUIView()
self.view.addSubview(popUp)
}
2) My UIView:
class MyUIView: UIView {
var interstitial: GADInterstitial!
let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("UIVIEW BUTTON", for: .normal)
btn.addTarget(self, action: #selector(prepareInterstitial), for: .touchUpInside)
return btn
}()
#objc func prepareInterstitial(_ sender: UIButton) {
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
let request = GADRequest()
interstitial.load(request)
dismissPopUp()
if interstitial.isReady {
interstitial.present(fromRootViewController: MyViewController())
}
}
I get this in the console:
Warning: Attempt to present <GADFullScreenAdViewController: 0x7f8611e22fc0> on <Project.MyViewController: 0x7f8612884800> whose view is not in the window hierarchy!`
which I do not understand because MyViewController is still very much a part of the view hierarchy.
I'd be really grateful if someone could show me how to fix this error, I'm relatively new to coding and not sure what I am doing wrong. Thank you!

The reason why this doesn't work is because you are creating a brand new VC here:
interstitial.present(fromRootViewController: MyViewController())
This MyViewController() is not the VC that is shown on the screen! You just created by calling its initialiser.
You need to somehow get the VC that's shown on the screen. One simple way to do this is to add a rootVC property to your MyUIView:
weak var rootVC: UIViewController?
And then present this instead:
if let rootVC = self.rootVC { // nil check!
interstitial.present(fromRootViewController: rootVC)
}
In showUIView, set self as rootVC:
#objc func showUIView(_ sender: UIButton) {
let popUp = MyUIView()
popUp.rootVC = self
self.view.addSubview(popUp)
}

Related

How to navigate from one View Controller to the other?

I want to navigate from one View Controller to another.
let vc = SecondViewController()
I have tried until now :
vc.modalPresentationController = .fullScreen
self.present(vc, animated: true) //self refers to the main view controller
Im trying to open a new ViewController when the users manages to register or to log in.I am new to software developing, and I want to ask, is this the best method to navigate from one ViewController to another, im asking because as I can see the mainViewController is not deinit(). I have found other similar questions and tried the answers, the problem is with the:
self.navigationController?.pushViewController
it doesn't work because I don't have any storyboard.
The question is it is right to navigate as explained above?
Thanks,
Typically when you are doing login you would use neither push or present. There are multiple ways of handling this, but the easiest is to embed in some parent (root) VC. Here is an example:
class ViewController: UIViewController {
private var embeddedViewController: UIViewController! {
didSet {
// https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller
// Add the view controller to the container.
addChild(embeddedViewController)
view.addSubview(embeddedViewController.view)
// Create and activate the constraints for the child’s view.
embeddedViewController.view.translatesAutoresizingMaskIntoConstraints = false
embeddedViewController.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
embeddedViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
embeddedViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
embeddedViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Notify the child view controller that the move is complete.
embeddedViewController.didMove(toParent: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let loginVC = LoginViewController()
loginVC.delegate = self
embeddedViewController = loginVC
}
}
extension ViewController: LoginDelegate {
func didLogin() {
embeddedViewController = MainViewController()
}
}
protocol LoginDelegate: AnyObject {
func didLogin()
}
class LoginViewController: UIViewController {
private lazy var loginButton: UIButton = {
let button = UIButton()
button.setTitle("Login", for: .normal)
button.addTarget(self, action: #selector(didTapLoginButton), for: .touchUpInside)
return button
}()
weak var delegate: LoginDelegate?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(loginButton)
view.backgroundColor = .red
loginButton.translatesAutoresizingMaskIntoConstraints = false
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc private func didTapLoginButton() {
delegate?.didLogin()
}
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
}

How do I set an image in a cell class from a ImagePicker Controller

I'm trying to change the image of a button to an image that the user picked in the imagePicker, but I am presenting the image picker in the ViewController class and the button is in the cell class.
I'm not sure how to set it from a different class. Here is the code I have for presenting the image picker and the way the button is created:
Here is my profileVC class:
class UserProfileVC: UICollectionViewController, UICollectionViewDelegateFlowLayout, UserProfileHeaderDelegate {
func handleEditBannerTapped(for header: ProfileHeader) {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
}
The above code is working fine and is presenting all pictures on the device for the user to choose.
Here is my ProfileHeader Cell class
class ProfileHeader: UICollectionViewCell, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
lazy var profileBanner: UIButton = {
let button = UIButton()
button.clipsToBounds = true
let imageTapped = UITapGestureRecognizer(target: self, action: #selector(handleBannerTapped))
imageTapped.numberOfTapsRequired = 1
button.isUserInteractionEnabled = true
button.addGestureRecognizer(imageTapped)
return button
}()
#objc func handleBannerTapped() {
delegate?.handleEditBannerTapped(for: self)
}
}
How do I set the image that the user picks to the profileBanner Button from the UserProfileVC class?
I'm assuming UserProfileVC is the main class, which contains a collection view, and ProfileHeader is a cell inside that collection view? You need to know the IndexPath of the cell, then do:
if let cell = collectionView.cellForItem(at: indexPath) as? ProfileHeader {
cell.profileBanner.setImage(imageThatYouWant, for: .normal)
}

How can I make sure that after declaring a delegate of a protocol in Swift 5.2, that delegate is not nil when it is called?

I am trying to clear a textfield in MainViewController from the DetailViewController. I have the following code in a Swift Playground.
import UIKit
import PlaygroundSupport
protocol DetailViewControllerDelegate: class {
func bar()
}
class DetailViewController: UIViewController {
var detailViewControllerDelegate: DetailViewControllerDelegate!
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let button = UIButton()
button.frame = CGRect(x: 100, y: 200, width: 180, height: 20)
button.setTitle("Hello World!", for: .normal)
button.backgroundColor = .blue
button.addTarget(self, action: #selector(handlePress), for: .touchUpInside)
view.addSubview(button)
self.view = view
}
#objc func handlePress() {
print("\(#function)")
if let vrvc = detailViewControllerDelegate {
vrvc.bar()
} else {
print("detailViewControllerDelegate is NIL")
}
}
}
class MainViewController : UIViewController, DetailViewControllerDelegate {
func bar() {
print("Inside Bar")
}
override func loadView() {
let detailViewController = DetailViewController()
detailViewController.detailViewControllerDelegate = self
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = DetailViewController()
When the button is pressed, detailViewController is nil. How can I make sure that detailViewController is NOT nil when the button is pressed?
You have two distinct DetailViewControllers:
override func loadView() {
let detailViewController = DetailViewController()
detailViewController now references a new instance of DetailViewController
detailViewController.detailViewControllerDelegate = self
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = DetailViewController()
liveView now references a new instance of DetailViewController which is different from the one above and in which you have not set detailViewControllerDelegate.
If you need to create a DetailViewController in one place and reference it in another you need to store a reference to it in a property that is accessible in both places.
That said, the second instance of DetailViewController is being created in a statement which looks like an attempt to test code in the Playground so maybe you just need to think about how you are testing.

Swift Custom Navigation Bar Item With Observer

So I have this custom navigation bar item that I would like to subclass out and use in other ViewControllers. The issue is that the Bar Item also has an observer that I want to add on ViewDidAppear and remove the observer on ViewDidDisappear. How can I subclass the bar item along with the observer out. And include it in each of my ViewControllers instead of duplicating code?
Current Code:
In each ViewController:
override func viewWillAppear(animated: Bool) {
if (!observingDefaults) {
defaults.addObserver(self, forKeyPath: Constants.General.kConnectedToPC, options: NSKeyValueObservingOptions(), context: nil)
observingDefaults = true
}
self.updateConnectionIcon()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if (observingDefaults) {
defaults.removeObserver(self, forKeyPath: Constants.General.kConnectedToPC)
observingDefaults = false
}
}
My Extension For The Nav Bar Item:
extension UIViewController {
func updateConnectionIcon() {
self.navigationItem.rightBarButtonItem = nil
let isConnected = NSUserDefaults.standardUserDefaults().boolForKey(Constants.General.kConnectedToPC)
let button = UIButton()
if isConnected {
button.setImage(UIImage(named: "BluetoothEnabled")?.imageWithRenderingMode(.AlwaysTemplate), forState: UIControlState.Normal)
} else {
button.setImage(UIImage(named: "BluetoothDisabled")?.imageWithRenderingMode(.AlwaysTemplate), forState: UIControlState.Normal)
}
button.frame = CGRectMake(0, 0, 23, 31)
button.tintColor = UIColor.whiteColor()
button.userInteractionEnabled = false
let barButton = UIBarButtonItem(customView: button)
self.navigationItem.rightBarButtonItem = barButton
}
}
Anyway I can add the top half to the bottom code so I can just call updateConnectionIcon() in each ViewController?
Rather than putting observer in each viewController, create one BaseClase and keep it there and inherit all class which needs these observer. is that make sense?

How to highlight the background of a UIButton when it is clicked and "unhighlight" it?

I am trying to create 4 UIButtons that highlight and stay highlighted when they are clicked. The only problem is I need only one UIButton to be Highlighted at a time. So, if there is a UIButton highlighted already, I need it to be "unhighlighted" and highlight the UIButton I clicked. I have tried to do this before and failed. Please help me with this problem.
I am using the Swift coding language to do this.
Any input or suggestions would be greatly appreciated.
If you give this answer an upvote, remember to upvote dasblikenlight's answer as well.
class ViewController: UIViewController {
// Connect all 4 buttons to this outlet
#IBOutlet var radioGroup: [UIButton]!
// Connect this action to all 4 buttons
#IBAction func radioGroupClicked(sender: AnyObject) {
// Unhighlight all buttons
unhighlightRadioGroup()
// Highlight the one being clicked on
highlightRadioGroup(sender as! UIButton)
}
// Set all 4 buttons in unselected state
func unhighlightRadioGroup() {
for button in radioGroup {
button.selected = false
}
}
// Set one button in the selected state
func highlightRadioGroup(button : UIButton) {
button.selected = true
}
}
You can do it with an IBOutletCollection. Command-drag one of the buttons into the view controller code, and choose creating of an IBOutletCollection on drop, and name your collection something - say, radioGroup. Then control-drag the remaining three buttons into the same IBOutletCollection.
Next thing is to add a method to un-highlight all buttons in your radioGroup. This can be done with a simple loop.
Finally, add calls to unhighlightRadioGroup from the event handler of your buttons. Event handler should first call your unhighlightRadioGroup method, and then highlight the sender received in the event handler.
lazy var buttonsArray: [UIButton] = {
var buttons = [UIButton]()
let firstButton = UIButton()
let secondButton = UIButton()
let thirdButton = UIButton()
let fourthButton = UIButton()
buttons = [firstButton, secondButton, thirdButton, fourthButton]
return buttons
}()
private func setupButtonMethods() {
filteredButtons[0].addTarget(self, action: #selector(firstButtonPressed(sender:)), for: .touchUpInside)
filteredButtons[1].addTarget(self, action: #selector(secondButtonPressed(sender:)), for: .touchUpInside)
filteredButtons[2].addTarget(self, action: #selector(thirdButtonPressed(sender:)), for: .touchUpInside)
filteredButtons[3].addTarget(self, action: #selector(fourthButtonPressed(sender:)), for: .touchUpInside)
}
private func setupHiglightedStateOnButton(button: UIButton) {
for btn in buttonsArray {
btn.isSelected = false
btn.backgroundColor = .gray
btn.setTitleColor(.white, for: .normal)
btn.isUserInteractionEnabled = true
}
button.isSelected = true
button.backgroundColor = .yellow
button.setTitleColor(.black, for: .normal)
button.isUserInteractionEnabled = false
}
#objc func firstButtonPressed(sender: UIButton) {
setupHiglightedStateOnButton(button: sender)
}
#objc func secondButtonPressed(sender: UIButton) {
setupHiglightedStateOnButton(button: sender)
}
#objc func thirdButtonPressed(sender: UIButton) {
setupHiglightedStateOnButton(button: sender)
}
#objc func fourthButtonPressed(sender: UIButton) {
setupHiglightedStateOnButton(button: sender)
}
remember to call setupButtonMethods() inside viewDidLoad()