How Can I dismiss a ViewController from a popup callback - appconkit

I am creating a popup message and show this popup over an action from a button.
Now I am trying to dismiss the SplitViewController, on that the popup is shown, from the popup callback.
But in my case it fails, so how is the right way to do this?
function showPopUp
{
var popup = vMobileController.solutionController().createPopUp();
popup.setMessage("Testmessage?");
popup.setTitle("test");
popup.addOption("yes",popupYES);
popup.addOption("no",popupNO);
popup.show();
}
function popupNO()
{
var vSolutionController = vMobileController.solutionController();
var vDatamanager = vMobileController.dataManager();
var vLogger = vMobileController.logger();
var currViewC = vSolutionController.rootViewController().firstSubViewControllerByName("overview_split");
currViewC.dismissModal();
}
function popupYES()
{
}

A modal presented controller is never a sub controller of the root view controller. To access modal presented controllers use the accessing methods of the solution controller itself.
var vSolutionController = mobileController.solutionController();
var vControllertoDismiss = vSolutionController.findFirstViewController("overview_split");
if (vControllertoDismiss) {
vControllertoDismiss.dismissModal();
} else {
logger.debug("Coun't find controller overview_split");
}
If this also not find your controller you might misspelled the controllers name? Use Screens.overview_split to let the ACK autocomplete the screen name.
var vControllertoDismiss = vSolutionController.findFirstViewController(Screens.overview_split);

Related

How to test if the app is presenting a certain view controller?

I'm pretty new to XCode UI tests and I'm trying to run a test where I fill two text labels and then I press a button. After the button is pressed the app should make an URL call and be redirected to another view controller. I want to check if at the end of this operation the second view controller is displayed.
To test this, I have written the following test:
let app = XCUIApplication()
app.launch()
let ownerTextField = app.textFields["ownerTextField"]
ownerTextField.tap()
ownerTextField.typeText("UserA")
let repositoryTextField = app.textFields["repositoryTextField"]
repositoryTextField.tap()
repositoryTextField.typeText("AppB")
app.buttons["SearchButton"].tap()
XCTAssertTrue(app.isDisplayingResults)
Where isDisplayingResults is
extension XCUIApplication {
var isDisplayingResults: Bool {
return otherElements["resultView"].exists
}
}
I have set up the identifier of the View controller inside its swift file class:
override func viewDidLoad() {
super.viewDidLoad()
view.accessibilityIdentifier = "resultView"
...
Nonetheless to say, the test fails. How can I get a success?
It's so simple.
If after clicking the URL, Viewcontroller is presenting means your previous VC button doesn't exist on screen.
So Just check for previous VC button exists or not.
If app.buttons["SearchButton"].esists()
{ //write if code
} else {
// Write else code
}

SwiftUI: Admob Interstitial Ad is not being presented on rootViewController

CODED IN SWIFTUI
I have implemented a settings page within my SwiftUI app that is presented upon clicking the button within the view. This view is wrapped within a sheet. You can see the code below:
TabsBar(showOptionsTab: $showOptionsTab)
.sheet(isPresented: $showOptionsTab)
{
OptionsTab(showOptionsTab: self.$showOptionsTab)
}
I have implemented an interstitial within this "OptionsTab" that is created when the options tab is loaded within the .onAppear() call and presented when the back button is selected. You can the code from the OptionsTab below:
#State private var interstitial : GADInterstitial!
VStack
{
... other code
Button(action:
{
if ( self.interstitial.isReady)
{
let root = UIApplication.shared.windows.first?.rootViewController
self.interstitial.present(fromRootViewController: root!)
}
self.showOptionsTab.toggle()
})
{
Image(systemName: "xmark")
.font(.largeTitle)
}
}
.onAppear
{
self.interstitial = GADInterstitial(adUnitID: "addID")
let req = GADRequest()
self.interstitial.load(req)
}
I have additional code within the button that logs the status of the interstitial, and it shows it as being READY, and the present code is being hit... but it never presents itself.
I have implemented this exact code in the ContentView, and the interstitial loads perfectly fine. Something about this code being within the sheet is causing the interstitial not to present.
Is the rootViewController the incorrect view to load the interstitial on when in a view that is presented from a sheet? Otherwise, what else am I doing wrong?
Thanks for the help in advance!
Try this
TabsBar(showOptionsTab: $showOptionsTab)
.sheet(isPresented: $showOptionsTab)
{
OptionsTab(showOptionsTab: self.$showOptionsTab)
.OnDisapear
{
if ( self.interstitial.isReady)
{
let root = UIApplication.shared.windows.first?.rootViewController
self.interstitial.present(fromRootViewController: root!)
}
}
}
}
And then add the state interstitial variable within this view, and onAppear of the ContextView, create the interstitial there.
Solution 2:
(not tested but should work):
present on the presentingViewController instead of the rootViewController
if ( self.interstitial.isReady)
{
let root = UIApplication.shared.windows.first?.rootViewController?.presentingViewController
self.interstitial.present(fromRootViewController: root!)
}
check the reason, at the end of the answer
Solution 1:
it sounds like that there's something that prevents Interstitial ad to be presented when a sheet view is already presented
in my case i wanted to present the ad immediately after the sheet view is presented, which didn't work
what worked is, Presenting the ad, and after it being dismissed, the sheet view will be presented , and it worked!
for the Interstitial i used this code
import SwiftUI
import GoogleMobileAds
import UIKit
final class Interstitial:NSObject, GADInterstitialDelegate{
var interstitialID = "ca-app-pub-3940256099942544/4411468910"
var interstitial:GADInterstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
var sheet: (() -> Void)? = nil //this closure will be executed after dismissing the ad
override init() {
super.init()
LoadInterstitial()
}
func LoadInterstitial(){
let req = GADRequest()
self.interstitial.load(req)
self.interstitial.delegate = self
}
func showAd(then sheet:(() -> Void)?){
if self.interstitial.isReady{
let root = UIApplication.shared.windows.first?.rootViewController
self.interstitial.present(fromRootViewController: root!)
self.sheet = sheet
}
else{
print("Not Ready")
}
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
self.interstitial = GADInterstitial(adUnitID: interstitialID)
LoadInterstitial()
if let presentSheet = sheet {
presentSheet()
}
}
}
here we pass whatever we want to happen to the closure which will be called when interstitialDidDismissScreen is called
now instead of switching the state when button is pressed, it will be switched after the ad is dimissed
at the top of your contentView:
struct HistoryView: View {
private var fullScreenAd: Interstitial!
init() {
fullScreenAd = Interstitial()
}
}
then in your button:
Button(action: {
self.interstitial.showAd {
self.showOptionsTab.toggle()
}
})
Note: i used the code of interstitial Ad from this gist
Also, AFAIK we can't tell if it's an bug in swiftUI, or it's something googleAdmob was supposed to take care of ?, there's no SwiftUI support From google so we can't blame them.
the problem AFAIK, that in iOS 13, grabbing the root ViewController will return the View Controller that presented the sheet, and when trying to present the ad, UIKit will complain that you are trying to present A view controller while a view controller is already present(the sheet),
Solution ? simple, present the Ad, on the presented view Controller(The sheet)

Button text not changing when called in viewController init

I'm trying to change the text of a UIButton based on values that are set in my viewController init.
So here's the init:
init(viewModel: ViewModel, stateMachine: StateMachine) {
self.viewModel = viewModel
self.stateMachine = stateMachine
super.init(nibName: nil, bundle: nil)
title = "TEST"
setInitialButtonText()
}
The button setting method:
private func setInitialButtonText() {
if viewModel.order.measurement.total != nil {
button.title = "Review"
} else {
button.title = "Check"
}
}
Required values are loaded in viewModel.
Now when I call this button method in this init, it has no effect. Yet when I call it in viewDidLoad, it works.
The button code is as follows:
lazy var button: UIButton = {
let button = UIButton(title: "Test", color: UIColor.green)
button.alpha = 1
return button
}()
init(#escaping buttonAction) {
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
stackView.addButtons([button])
button.pressedAction = buttonAction
}
When I put the method into init, the default initialised value of 'Test' shows on the button. However, when I print the button title from both init and viewDidLoad, first the viewDidLoad one is called and shows 'Test' and later the init one is called and shows the correct value. If this is called after viewDidLoad, why does it not reflect on the UI?
When I insert the method into viewDidLoad, the correct value is displayed...
Sure I'm missing something obvious here but any help would be appreciated.
UPDATE:
I printed from viewDidDisappear as well and the correct value is printing even when I call the method from init. So the property is being set but not displayed on the button...
Not sure whether that's the problem. But have you tried to set the title of the button by using button.setTitle("myTitle", for: .normal)?
None of the code above shows you actually adding the button to the view controller's view - without seeing more it's difficult to tell, but is it possible that you are also creating and adding the button in a xib, and assigning it to the button property? This would replace the button you have created in the lazy load.
Alternatively, is there a totally separate button that you are creating, but is never getting updated?
How does button get added to the view controller's view?
In the end I've added a new argument to the init as follows:
init(#escaping buttonAction, buttonTitle: String) {
self.buttonTitle = buttonTitle
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
stackView.addButtons([button])
button.pressedAction = buttonAction
}
Then in the button variable:
let button = I6Button(title: reviewFuelSheetButtonTitle, color: UIColor.i6.green)
And then finally in the viewController I added a didSet on the viewModel:
private var viewModel: ViewModel {
didSet {
setInitialButtonText()
}
}

How to check if UIViewController is already being displayed?

I'm working on an app that displays a today extension with some information. When I tap on the today extension, it opens the app and navigates to a subview from the root to display the information. Normally the user would then click the back arrow to go back to the main view, but there is no way to tell if this is actually done. It is possible for the user to go back to the today extension and tap again. When this is done, the subview is opened once again with new information. If this is done a bunch of times, I end up with a bunch of instances of the subview and I have to click the back button on each of them to get back to the main view.
My question: Is it possible to check if the subview is already visible? I'd like to be able to just send updated information to it, instead of having to display an entirely new view.
I am currently handling this by keeping the instance of the UIViewController at the top of my root. If it is not nil, then I just pass the information to it and redraw. If it is nil, then I call performSegue and create a new one.
I just think that there must be a better way of handling this.
Edit: Thanks to the commenter below, I came up with this code that seems to do what I need.
if let quoteView = self.navigationController?.topViewController as? ShowQuoteVC {
quoteView.updateQuoteInformation(usingQuote: QuoteService.instance.getQuote(byQuoteNumber: quote))
}
else {
performSegue(withIdentifier: "showQuote", sender: quote)
}
This is different from the suggested post where the answer is:
if (self.navigationController.topViewController == self) {
//the view is currently displayed
}
In this case, it didn't work because I when I come in to the app from the Today Extension, it goes to the root view controller. I needed to check whether a subview is being displayed, and self.navigationController.topViewcontroller == self will never work because I am not checking to see if the top view controller is the root view controller. The suggestions in this post are more applicable to what I am trying to accomplish.
u can use this extension to check for currently displayed through the UIApplication UIViewController:
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
and usage example:
if let topController = UIApplication.topViewController() {
if !topController.isKind(of: MainViewController.self) { //MainViewController- the controller u wish to equal its type
// do action...
}
}

when button pressed or when view Disappear in swift

I have 3 view controller.
First view controller has a button and second view controller has a button.
My third view controller has 2 buttons (button 1 and button 2) when the button of first view controller or second view controller pressed it shows the third view controller, I want to write a code that:
If first view controller button pressed or view disappear my third view controller button 1 hide else second view controller button pressed or view disappear my third view controller button 2 hide.
I tried many ways in swift but it doesn't work at all, can any help me with that please...
Use like this : creates a new instance of ViewController.
class ViewController1 : UIViewController
{
...
func goToSecondView() {
var viewController = ViewController2()
viewController. isFrom = 1
self.navigationController.pushViewController(viewController, animated: true)
}
}
class ViewController3 : UIViewController
{
var isFrom : Int?
override func viewDidLoad()
{
if isFrom == 1
{
button.hidden = true Or button.enable = false
}
else isFrom == 2
{
button2.hidden = true Or button2.enable = false
}
}
...
}