How to make the page transition after clicking the button [duplicate] - swift

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 1 year ago.
I'm developing an app to log book that children have read. So I would like to know how to make the page transition back to the menu page after clicking the "save" button. I also want to make the alert that shows "Data has been saved!". Below are my codes.
#IBOutlet weak var newBookSaveButton: UIButton!
#IBAction func newBookTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid,
let data = bookData() else {
return
}
db.collection("new reading").document(uid).setData(data)
}
func bookData() -> [String: Any]? {
guard let title = bookTitleTextField.text,
let author = bookAuthorTextField.text,
let summary = bookSummaryTextField.text else {
return nil
}
let data: [String: Any] = [
"bookTitle": title,
"bookAuthor": author,
"bookSummary": summary
]
return data
self.transitionToMenu()
}
func transitionToMenu() {
let MenuViewController = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.MenuViewController) as? MenuViewController
view.window?.rootViewController=MenuViewController
view.window?.makeKeyAndVisible()
}
}
With this code, I still unable to transition back to the Menu page. Your help are very much appreciated.

You can use this function:
func transitionToMenu() {
let alert = UIAlertController(title: nil, message: "Data has been saved!", preferredStyle: .alert)
alert.view.alpha = 0.5
alert.view.layer.cornerRadius = 15
self.present(alert, animated: true)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
alert.dismiss(animated: true)
if let navController = self.navigationController {
navController.popViewController(animated: true)
} else {
self.dismiss(animated: true, completion: {})
}
}
}

Related

Trying to switch which photo pops up depending on button clicked Xcode

I am making an app that will display a random quote from a stoic philosopher. Right now, I am stuck on trying to make the correct picture pop up. (User clicks on a Button with the philosopher's name on it, and then a new view pops up with an image of the philosopher and a random quote by him).
class ViewController: UIViewController {
var allQuotes = [String]()
var pictures = [String]()
#IBOutlet var Epictetus: UIButton!
#IBOutlet var Seneca: UIButton!
#IBOutlet var MarcusAurelius: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Create a constant fm and assign it the value returned by FileManager.default (built in system type)
let fm = FileManager.default
// Declares a new constant called path that sets the resource path of ours apps buddle.
// A bundle is a directory containing our compiled program and all our assets
let path = Bundle.main.resourcePath!
// items array will be a constant collection of the names of all the files found in the directory of our app
let items = try! fm.contentsOfDirectory(atPath: path)
// create a loop to go through all of our items...
for item in items {
if item.hasSuffix("jpg"){
pictures.append(item)
}
}
print(pictures)
title = "Stoicism"
if let stoicQuotesURL = Bundle.main.url(forResource: "quotes", withExtension: "txt"){
if let stoicQuotes = try? String(contentsOf: stoicQuotesURL) {
allQuotes = stoicQuotes.components(separatedBy: "\n\n")
}
}
}
#IBAction func buttonTapped(_ sender: UIButton) {
if sender.tag == 0 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[0]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 1 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[1]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 2 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[2]
navigationController?.pushViewController(vc, animated: true)
}
}
}
}
That's the code for my main viewController.
import UIKit
class PictureViewController: UIViewController {
#IBOutlet var picture: UIImageView!
#IBOutlet var imageView: UIImageView!
var selectedImage: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let imageToLoad = selectedImage {
imageView.image = UIImage(named: imageToLoad)
}
}
override func viewWillAppear(_ animated: Bool) {
// doing it for the parent class
super.viewWillAppear(animated)
// if its a nav Cont then it will hide bars on tap...
}
// now make sure it turns off when you go back to the main screen
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
That's the code for the viewController that has the imageView. Right now, the image that's popping up is always the preset (Marcus Aurelius), even though my code looks correct to me. Obviously it isn't (also, I've already debugged and ensured through print statements that the jpg files add to the pictures array correctly).
Any help would be appreciated.
First of all, this code is really silly:
#IBAction func buttonTapped(_ sender: UIButton) {
if sender.tag == 0 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[0]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 1 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[1]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 2 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[2]
navigationController?.pushViewController(vc, animated: true)
}
}
}
Do you see that everything in those lines is identical except for the numbers? So make the number a variable:
#IBAction func buttonTapped(_ sender: UIButton) {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
print(sender.tag)
vc.selectedImage = pictures[sender.tag]
navigationController?.pushViewController(vc, animated: true)
}
}
See how much shorter and clearer that is? Okay, I've also added a print statement. This will print the tag to the console. You need to make sure that your buttons do have the right tags. If they do, your code should work.

having trouble trying to keep users logged in with addStateDidChangeListener()

So my goal for now is to successfully keep users logged in and show a certain viewController depending if they're logged in or not. I've read a lot of the Stack questions that showed up first on Google searches about this same topic and they said use addStateDidChangeListener() and that's exactly what I did.
I didn't know how to approach this, so I watched a Youtube video and copied the exact code the guy had, his project did what I wanted mine to do, so I gave it a shot. Unfortunately when I run the simulator, sign in, exit the simulator and simulate again, nothing changes. I will add my code and it's location.
This is the code in my AppDelegate.swift in the didFinishLaunchingWithOptions method
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let auth = Auth.auth()
auth.addStateDidChangeListener { (_, user) in
switch user {
case nil:
guard self.activeViewController! is StudentSegmentedTableViewController else { return }
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
self.navigationController.setViewControllers([nonLoggedInViewController], animated: false)
self.navigationController.popToViewController(nonLoggedInViewController, animated: true)
self.activeViewController = nonLoggedInViewController
default:
guard self.activeViewController! is GothereMainMenuViewController else { return }
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
self.navigationController.setViewControllers([alreadyLoggedInViewController], animated: false)
self.navigationController.popToViewController(alreadyLoggedInViewController, animated: true)
self.activeViewController = alreadyLoggedInViewController
}
}
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
activeViewController = nonLoggedInViewController
switch Auth.auth().currentUser != nil {
case true:
activeViewController = alreadyLoggedInViewController
default:
break
}
navigationController = UINavigationController.init(rootViewController: activeViewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
I tried just this alone at first, and it didn't work so then I implemented a state listener in reasonable spots in my app.
First I added one that enables right after successful log in/signup and the segue is performed .
func enableAuth() {
authListener = Auth.auth().addStateDidChangeListener { (_, user) in
print("State Listener activated")
}
}
This is what I call in the viewDidLoad() of the segued viewController right after login/signup. To remove it, I simply call it when the logout button is pressed..
func disableAuthState() {
Auth.auth().removeStateDidChangeListener(self.authListener!)
print("State Listener Deactivated")
}
func studentLogoutSelected() {
var text = UITextField()
let alert = UIAlertController(title: "Logout", message: "Are you sure you want to logout?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
self.dismiss(animated: true, completion: nil)
}
let logoutAction = UIAlertAction(title: "Logout", style: .default) { (logoutAction) in
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
self.disableAuthState()
self.performSegue(withIdentifier: Constants.Segues.studentLogout, sender: self)
} catch let signOutError as NSError {
print("There was an error signing the user out. \(signOutError)")
}
}
alert.addAction(cancelAction)
alert.addAction(logoutAction)
present(alert, animated: true, completion: nil)
}
After all these functions and implementations, the shown blocks of code still don't do what I expected them to do. If anybody can point out issues or suggestions, that would be great, thanks.
First of all are you add FirebaseApp.configure() on your didFinishLaunchingWithOptions function in appdelegate? Then, Can you try call enableAuth in viewWillAppear()

Hook On to Exit Event of Application and Show an Alert

I need to show an alert box when the user quits the application by clicking on the x button.How can I hook on to the exit event of the application and show an alert or view controller via a segue
performSegue(withIdentifier: "segue", sender: nil)
Please advice..
I know you want to use a segue to do this because they're just so convenient, but segues can't be done in a storyboard going from app delegate events like "applicationWillResignActive" (going to background) or "applicationWillBecomeActive" (becoming foreground again).
The correct way to do this would be via an alert. And you'd likely want to do this in applicationShouldTerminate, since you probably want to A) be able to abort quitting if you have a good reason not to quit just yet or B) give the user a choice whether or not to really quit.
Here's how it would look in swift 4:
var licenseWindowController : LicenseWindowController?
func dialogOKCancel(question: String, text: String) -> Bool {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
return alert.runModal() == .alertFirstButtonReturn
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
// to bring up a window from your storyboard...
let mainStoryboard = NSStoryboard.init(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
self.licenseWindowController = mainStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "LicenseWindowController")) as? LicenseWindowController
if let actualLicenseWC = self.licenseWindowController
{
actualLicenseWC.showWindow(self)
}
return .terminateCancel
}
}
Swift 3
var licenseWindowController : LicenseWindowController?
func dialogOKCancel(question: String, text: String) -> Bool {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
return alert.runModal() == NSAlertFirstButtonReturn
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplicationTerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
let mainStoryboard = NSStoryboard.init(name: "Main", bundle: nil)
self.licenseWindowController = mainStoryboard.instantiateController(withIdentifier: "LicenseWindowController") as? LicenseWindowController
if let actualLicenseWC = self.licenseWindowController
{
actualLicenseWC.showWindow(self)
}
return .terminateCancel
}
}
In your appdelegate you should be able to put your code into applicationWillTerminate and make the message appear there.
Edit: You would probably be better off using a modal alert instead of a segue.

How to send selected cell to another view controller?

Hey guys so I'm working on an application where I have imported a contacts list from my device and I am given the option to "add" the contact but it really doesn't do much when it comes to functionality. I'm not the best coder so try to hear me out. what I am trying to do is take the data/ selected table view cell and display it on another page. I "think" that this is what I should do because I have tried to display the data on another page but get an error when I move my OVERRIDE function. that makes me believe that I need to take the data, which I believe is newContact? and set that as a variable and then display it on a new page where I can create a new view controller and add the code without error.
I essentially need to figure out what my JSON data is saved as, then set that equivalent to a string if that is possible, so I can send it to my new view controller or and send it to my database with code I already have created.
I am just not sure where to enter the statements because of errors that I am getting and what the exact code would be.
Sorry for the awful description of what I am trying to perform, I have a grasp of what is needed to be done but I am a beginner.
My Master View Controller that takes the contacts from my phone and accesses them.
import UIKit
import Contacts
import ContactsUI
class MainViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var tableView: UITableView!
var store = CNContactStore()
var contacts: [CNContact] = []
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - User Actions
#IBAction func contactListPressed(_ sender: AnyObject) {
let contactPickerViewController = CNContactPickerViewController()
contactPickerViewController.delegate = self
present(contactPickerViewController, animated: true, completion: nil)
}
#IBAction func addContactPressed(_ sender: AnyObject) {
let newContact = CNMutableContact()
newContact.givenName = "Apps"
newContact.familyName = "Foundations"
newContact.nickname = "AF"
if let image = UIImage(named: "logo-apps-foundation.jpg"),
let data = UIImagePNGRepresentation(image){
newContact.imageData = data
}
let phone = CNLabeledValue(label: CNLabelWork, value: CNPhoneNumber(stringValue: "+441234567890"))
newContact.phoneNumbers = [phone]
let email = "" //Your Input goes here
let Email = CNLabeledValue(label:CNLabelWork, value: email as NSString)
newContact.emailAddresses = [Email]
newContact.jobTitle = "Apps Foundation"
newContact.organizationName = "Apps Foundation"
newContact.departmentName = "IT"
let facebookProfile = CNLabeledValue(label: "Facebook", value: CNSocialProfile(urlString: "https://www.facebook.com/appsfoundation", username: "AppsFoundation", userIdentifier: "appsfoundation", service: CNSocialProfileServiceFacebook))
let twitterProfile = CNLabeledValue(label: "Twitter", value: CNSocialProfile(urlString: "https://twitter.com/AppsFoundation", username: "AppsFoundation", userIdentifier: "appsfoundation", service: CNSocialProfileServiceTwitter))
newContact.socialProfiles = [facebookProfile, twitterProfile]
let skypeProfile = CNLabeledValue(label: "Skype", value: CNInstantMessageAddress(username: "AppsFoundation", service: CNInstantMessageServiceSkype))
newContact.instantMessageAddresses = [skypeProfile]
var birthday = DateComponents()
birthday.year = 1991
birthday.month = 1
birthday.day = 1
newContact.birthday = birthday
let request = CNSaveRequest()
request.add(newContact, toContainerWithIdentifier: nil)
do {
try store.execute(request)
let alert = UIAlertController(title: "Contacts iOS 9", message: "New contact has been created", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
} catch let error{
print(error)
}
}
#IBAction func textFieldValueChanged(_ sender: AnyObject) {
if let query = textField.text {
findContactsWithName(query)
}
}
//MARK: - Private Methods
func findContactsWithName(_ name: String) {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
DispatchQueue.main.async(execute: { () -> Void in
do {
let predicate: NSPredicate = CNContact.predicateForContacts(matchingName: name)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()] as [Any]
self.contacts = try self.store.unifiedContacts(matching: predicate, keysToFetch:keysToFetch as! [CNKeyDescriptor])
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
func updateContact(_ contactIdentifier: String) {
do {
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactPhoneNumbersKey, CNContactViewController.descriptorForRequiredKeys()] as [Any]
let contact = try store.unifiedContact(withIdentifier: contactIdentifier, keysToFetch:keysToFetch as! [CNKeyDescriptor])
let contactToUpdate = contact.mutableCopy() as! CNMutableContact
contactToUpdate.phoneNumbers = [CNLabeledValue(label: CNLabelWork, value: CNPhoneNumber(stringValue: "+440987654321"))]
let saveRequest = CNSaveRequest()
saveRequest.update(contactToUpdate)
try store.execute(saveRequest)
} catch let error{
print(error)
}
}
}
//MARK: - UITableViewDataSource
extension MainViewController: CNContactPickerDelegate {
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let selectedContactID = contact.identifier
updateContact(selectedContactID)
}
}
//MARK: - UITableViewDataSource
extension MainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier = "MyCell"
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier)
cell!.textLabel!.text = contacts[indexPath.row].givenName + " " + contacts[indexPath.row].familyName
if let birthday = contacts[indexPath.row].birthday {
let formatter = DateFormatter()
formatter.dateStyle = DateFormatter.Style.long
formatter.timeStyle = .none
cell!.detailTextLabel?.text = formatter.string(from: ((birthday as NSDateComponents).date)!)
}
return cell!
}
}
//MARK: - UITableViewDelegate
extension MainViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let controller = CNContactViewController(for: contacts[indexPath.row])
controller.contactStore = self.store
controller.allowsEditing = false
self.navigationController?.pushViewController(controller, animated: true)
}
}
I know I need to incorporate something like this but I am not sure where or how to set the JSON data to a variable or the correct type and then incorporate code of this type
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! ViewControllerB
controller.selectedName = objects[indexPath.row]
}
}
}
sorry for the awful explanation. any help possible would be appreciated, I have been struggling for quite some time.
First of all, you need to have the other view controller that you are trying to pass data to. It can either be on the Interface Builder or done programmatically (I'll assume it's on the IB for now). Then you'll need to setup a segue between the Main VC and the Details VC and give it an identifier e.g. showDetail.
Next would be to determine the data that Details VC needs for it to work properly. You can have individual variables for each data item (e.g. name, age, phone, email, etc) but usually if there is a lot of info, it's best to use a data model. In your case, since you are trying to display contact info, you can simply reuse CNContact.
So you simply need a CNContact in your Details VC that you'll set before transitioning from Main VC in the prepareForSegue function. And to initiate the segue, all you have to do is call performSegue function.
Hope that at least gives you some direction

MWPhotoBrowser - how to implement delete photo functionality?

I am using MWPhotoBrowser for my app. I need to give delete functionality to my users. Is there any way we can implement delete a particular photo or multiple photos functionality?
Quick help needed.
I did this in swift by adding this extension from outside of the library:
extension MWPhotoBrowser {
public override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let _ = valueForKey("_gridController") as? MWGridViewController {
let leftButtonItem = editButtonItem()
//leftButtonItem.tintColor = QorumColors.ThemeWhite
navigationItem.leftBarButtonItem = leftButtonItem
} else {
navigationItem.leftBarButtonItem = nil
}
}
public override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if editing {
navigationItem.leftBarButtonItem?.title = "Delete"
//navigationItem.leftBarButtonItem?.tintColor = QorumColors.Nickname
displaySelectionButtons = true
title = "Delete Photos"
let gridController = valueForKey("_gridController") as! MWGridViewController
gridController.selectionMode = displaySelectionButtons
gridController.collectionView!.reloadData()
} else {
let nav = self.navigationController as! TempPresentVC
let photosToDelete = nav.selectedPhotos
let afterButtonPress = {
//self.navigationItem.leftBarButtonItem?.tintColor = QorumColors.ThemeWhite
self.displaySelectionButtons = false
self.updateNavigation()
let gridController = self.valueForKey("_gridController") as! MWGridViewController
gridController.selectionMode = self.displaySelectionButtons
gridController.collectionView!.reloadData()
}
guard photosToDelete.count > 0 else {
afterButtonPress()
return
}
let title = "Delete Photo"
let message = "Are you sure you want to delete these photos?"
let action = "Delete"
let cancelAction = "Cancel"
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let declineButton = UIAlertAction(title: cancelAction, style: .Default, handler: { (action: UIAlertAction) in
afterButtonPress()
})
let acceptButton = UIAlertAction(title: action, style: .Default, handler: { (action: UIAlertAction) in
afterButtonPress()
})
alert.addAction(declineButton)
alert.addAction(acceptButton)
UIApplication.topMostController().presentVC(alert) //private lib
}
}
}
class TempPresentVC: UINavigationController, MWPhotoBrowserDelegate {
var selectedPhotos = [Int]()
func photoBrowser(photoBrowser: MWPhotoBrowser!, photoAtIndex index: UInt, selectedChanged selected: Bool) {
if selected {
selectedPhotos.append(index.toInt)
} else {
selectedPhotos.removeObject(index.toInt)
}
}
}
This doesn't allow you to delete a photo in a single photo view mode, but starting from this code base that part would be easy to implement.
Beware, some of the stuff I use inside are private functions I wrote in my other classes, what they do should be clear though.