IOS navigationController?.popToRootViewController has wrong - ios

#IBAction func addInformation(_ sender: UIBarButtonItem) {
// check rateHourly has value
if let editedRateHourly = rateHourly.text {
print("editedRateHourly condition is \(editedRateHourly)")
if editedRateHourly != ""{
print("not nil")
// check edit value is number?
let num = Int(editedRateHourly)
if num != nil {
print("is num")
// add to database
UserDefaults.standard.set(editedRateHourly, forKey: "\(findDate())")
UserDefaults.standard.synchronize()
// back to last viewController
navigationController?.popToRootViewController(animated: true)
}else{
print("not num")
print("error alert push!!")
popErrorAlert()
}
}else {
print("nil")
print("editedRateHourly condition is nil")
popErrorAlert()
}
}
}
#IBAction func cannelInformationPage(_ sender: UIBarButtonItem) {
navigationController?.popToRootViewController(animated: true)
}
I want to create a new simple edit page. It's two problem for me that when I finish edition if-else will check the condition is correct or not and then save the data popToRootViewControlle. When I finish edition I click on "addInformation" BarButtonItem and I get UI wrong. the Other wrong is when I click on editField but I don't enter any condition. And then I click on "cannelInformationPage" UIBarButtonItem. It also get wrong.
It's what I get wrong
wrong information

Because it returns Bool
_ = navigationController?.popToRootViewController(animated: true)

_ = self.navigationController?.popViewController(animated: true)
If you want to come back to last view controller than you can try above line, hope its work for you.

Related

Segue is not being performed

I've created a method for auto login via Firebase but somehow my segue is not being performed..
I've this code and in my viewDidLoad I'm calling the method (ofc)
override func viewDidLoad() {
super.viewDidLoad()
//Login user automatically
autoLogin()
}
func autoLogin(){
if Auth.auth().currentUser?.email != nil{
print("not nil") //for test
self.performSegue(withIdentifier: "toRootVc", sender: self)
print("not nil1") //for test
}
else{
print("nil") //for test
}
}
The app prints both "not nil" and "not nil1" but it still does not
performing the segue.
I also have a login button which works.
func handleLogin(){
Auth.auth().signIn(withEmail: email.text!, password: password.text!) { (result, err) in
if let err = err{
print("Error logging in:", err.localizedDescription)
}
else{
self.databaseHandler.retrieveData(email: self.email.text!){
self.performSegue(withIdentifier: "toRootVc", sender: self)
}
}
}
}
But the autoLogin doesn't actually performing the segue. (Ignores the step)
Any input would be much appreciated.
Segues won't work inside viewDidLoad as it's too early try in viewWillAppear or better do this check before presenting that vc say inside didFinishLaunchingWithOptions of AppDelegate
have you tried to put this autoLogin() on viewDidAppear()

Using TabViewController, When saving data, TableView not updating

My current setup: TabViewController that is connected to two TableViewControllers embedded in navigation controllers. I am able to update the tableview no problem, on app load and when switching back between the different tabs.
My problem comes when I open another tableviewcontroller to edit. When I hit save from this view controller, it updates the data and saves everything just fine however, My tableView will not update no matter what I try unless I switch between the different tabs or close and reopen the app.
I have tried using delegates to trigger the update, I have tried use NSNotificationCenter, and no dice.
Has anybody else had this issue?
Here is my save function:
func saveDose(completion: (_ finished: Bool) -> ()) {
if searchName == "" {
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let dose = Dose(context: managedContext)
let doseNumberString = numberDosesText.text
let doseNumberInt = Int64(doseNumberString!) ?? 0
dose.name = nameText.text
dose.script = scriptText.text
dose.dosage = dosageText.text
// dose.doseInterval = doseInterval
dose.firstDose = datePicker.date
dose.numberDoses = doseNumberInt
dose.doseReminder = remindersSwitch.isOn
do {
try managedContext.save()
print("Data Saved")
completion(true)
} catch {
print("Failed to save data: ", error.localizedDescription)
completion(false)
}
} else {
update(name:searchName, firstDose: searchDate)
completion(true)
}
}
And here is where I call it and load back to my other tableview.
#IBAction func saveBtnPressed(_ sender: UIBarButtonItem) {
saveDose { (done) in
if done {
print("We need to return now")
navigationController?.popViewController(animated: true)
self.dismiss(animated: true, completion: nil)
} else {
print("Try again")
}
}
}
And here is where I reload my tableview when the view appears
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("View has been loaded back from other view")
self.fetchData()
self.tableView.reloadData()
print("View will appear")
}
And here is my fetchData and loadData functions
func fetchData() {
loadData { (done) in
if done {
setEmptyView(Array: logArray.count)
}
}
}
func loadData(completion: (_ complete: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Log")
do {
logArray = try managedContext.fetch(request) as! [Log]
print("Data Fetched No Issues")
completion(true)
} catch {
print("Unable to fetch data: ", error.localizedDescription)
completion(false)
}
}
Again I have tried delegates and have used this code in other applications that have worked fine. Is it something to do with the tab bar controller?
The data is obviously saving fine, I just can't get the tableView to update properly.
It seems like it is not calling the viewWillAppear function after the save. I have tried using delegates as well to force the update of the tableview but nothing has been working.
Basically you need to call tableView.reloadData(). after you have done saving.
Moreover you can use beginUpdates, endUpdates on tableView to perform animations on specific rows. for more information on this you may refer to This Link
Reload your tableview in custom delegates method your problem will be solved.

how to dismiss alert view after validation?

I had used SCLAlertView used for forgot password in this i had placed a textfield to enter email so that after making successful validation only it need to hide without this it should not hide can anyone help me how to implement this ?
My code is shown below
#IBAction func forgetPasswordButton(_ sender: Any) {
let appearance = SCLAlertView.SCLAppearance(showCloseButton: true)
let alert = SCLAlertView(appearance: appearance)
let txt = alert.addTextField("Enter your emailid")
_ = alert.addButton("Submit", action: {
let action = txt.text
print(action as Any)
})
if(txt.text?.isEmpty)! == true{
}else{
}
_ = alert.showEdit("Forgot Password", subTitle:"Please enter your email address below.You will receive a link to reset your password",closeButtonTitle: "Cancel")
}
use the property of hideView() and call like
let action = txt.text
print(action as Any)
alert.hideView()
})
for more reference you can get the sample here

How to get user's phone number?

I have just started using Digits - Twitter API for Phone Number verification, but it seems I'm unable to read the user's Phone number, I'm not sure if there is a function for that or so, but after reading a while I knew that I can do that with a Call back after successful phone verification but no explanation for that !
AuthConfig.Builder authConfigBuilder = new AuthConfig.Builder()
.withAuthCallBack(callback)
.withPhoneNumber(phoneNumberOrCountryCodeFromMyActivity)
found this snippet but again not sure where to implement it.
HERE is my Action for the login button with phone verification:
fileprivate func navigateToMainAppScreen() {
performSegue(withIdentifier: "signedIn", sender: self)
}
#IBAction func tapped(_ sender: Any) {
let configuration = DGTAuthenticationConfiguration(accountFields: .defaultOptionMask)
configuration?.appearance = DGTAppearance()
configuration?.appearance.backgroundColor = UIColor.white
configuration?.appearance.accentColor = UIColor.red
// Start the Digits authentication flow with the custom appearance.
Digits.sharedInstance().authenticate(with: nil, configuration:configuration!) { (session, error) in
if session != nil {
// Navigate to the main app screen to select a theme.
self.navigateToMainAppScreen()
} else {
print("Error")
}
}
}
So I found the answer after digging a lot more in Digits Documentations and it was pretty simple, I had to add:
print(session.phoneNumber)
print(session.userID)
In the didTap function, so the complete code will be:
#IBAction func tapped(_ sender: Any) {
let configuration = DGTAuthenticationConfiguration(accountFields: .defaultOptionMask)
configuration?.appearance = DGTAppearance()
configuration?.appearance.backgroundColor = UIColor.white
configuration?.appearance.accentColor = UIColor.red
// Start the Digits authentication flow with the custom appearance.
Digits.sharedInstance().authenticate(with: nil, configuration:configuration!) { (session, error) in
if session != nil {
//Print Data
print(session?.phoneNumber)
print(session?.userID)
// Navigate to the main app screen to select a theme.
self.navigateToMainAppScreen()
} else {
print("Error")
}
}
}
Here is the Reference I have used:
https://docs.fabric.io/apple/examples/cannonball/index.html#sign-in-with-digits

Login with FBSDK, cannot perform segue

I have installed the Facebook SDK, followed all the requirements, it works as I can extract data from the user's Facebook account, however I cannot perform the segue once logged in.
I get an error message:
Warning: Attempt to present <viewController1> on <FBSDKContainerViewController> whose view is not in the window hierarchy!
Here are my codes:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (FBSDKAccessToken.currentAccessToken() == nil) {
print("not logged in")
}
else {
print("logged in")
}
let loginButton = FBSDKLoginButton()
loginButton.readPermissions = ["public_profile", "email", "user_friends"]
loginButton.center = self.view.center
loginButton.delegate = self
self.view.addSubview(loginButton)
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if ((error) != nil)
{
}
else if result.isCancelled {
}
else {
if result.grantedPermissions.contains("email")
{
// Do work
returnUserData()
addViewHierarchy()
// self.performSegueWithIdentifier("signUpFollowPage", sender: self)
}
}
}
Anyone can help ?
Thanks
I've been struggling with the same problem (although I'm using FBSDKLoginManager's logInWithReadPermissions).
Whilst the problem seems to be that the callback (or your didCompleteWithResult delegate call) is invoked before the Safari web view is dismissed, I can't find a nice solution.
My (horrific) answer has been to delay the segue, to give the FBSDKContainerViewController time to be dismissed naturally. In your case:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), { () -> Void in
self.performSegueWithIdentifier("signUpFollowPage", sender: self)
}
It may be possible to get away with a shorter delay.
This strikes me as a bug with the Facebook Login SDK. If I find time I might look at the SDK source. Fortunately the App I'm working on is not due for release until the new year, so I'm hoping that the issue might be fixed in a later version anyway.
You should use the viewDidAppear() function and check the FBSDKAccessToken.currentAccessToken :
override func viewDidAppear(animated:Bool){
if FBSDKAccessToken.currentAccessToken() != nil{
performSegueWithIdentifier("YOUR_SEGUE",sender:self)
}
}
I have faced the same issue while logging in with FB. Here is how I solved it. The issue is while navigating, the FBContainerView is still open. Using self.controller will refer to the FBContainerView and NOT the Topmost controller and hence no navigation. We will need to switch back to the top Most Controller and then perform the navigation.
You can do this without performing segue, by presenting view controller from top controller like this
let topController = self.topMostController()
topController.presentViewController(secondController, animated: true, completion: nil)
to get the top most controller define a method which will always return topmost controller
func topMostController()-> UIViewController {
var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
while((topController?.presentedViewController) != nil){
topController = topController?.presentedViewController
}
if(topController != nil){
return topController!
}
else{
return self
}
}
My solution is similar to the ones above, but it is a bit more deterministic (and for me, safer).
This solution ensures we ARE the top most VC before presenting.
Timer.scheduledTimer(withTimeInterval: 0.01 / 30.0, repeats: true, block: { (timer) in
if let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window, let rootVC = window.rootViewController {
var topController = rootVC
while let nextTop = topController.presentedViewController {
topController = nextTop;
}
if topController == self {
// we're good, go for it
self.performSegue(withIdentifier: "afterLoginSegue", sender: nil)
timer.invalidate()
} else {
log.debug("waiting to transition until we're presenting...")
}
} else {
log.error("couldn't resolve a key variable needed to segue, aborting.")
timer.invalidate()
}
})
I still feel like there should be a way to get a callback once the presenting facebook login safari VC is gone, but until that solution comes along, this is as good as i can do (obviously this code could be cleaned up and modularized for use in multiple VCs, but you can just dump this in where needed).

Resources