For some reasons, I can't seem to get my NSUserDefaults to work correctly under Swift 2.0. It work fine under the older version, but under Swift 2.0, it doesn't work. I know the coding has been changed for Swift 2.0, but for some reason, all the information filled in the tableview goes away once I leave that page. Any suggestions?
import UIKit
class MainTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var name: UITextField!
#IBAction func btnSave() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
let userDefaults = NSUserDefaults.standardUserDefaults()
NSUserDefaults.standardUserDefaults().setObject(name, forKey:"name")
userDefaults.synchronize()
}
override func viewDidLoad() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(" ", forKey: "name")
super.viewDidLoad()
}
}
Perhaps try this
import UIKit
class MainTableViewController: UITableViewController, UITextFieldDelegate {
let userDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var name: UITextField!
#IBAction func btnSave() {
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} else {
userDefaults.setObject(name.text, forKey:"name")
userDefaults.synchronize()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
name.text = userDefaults.stringForKey("name")
}
}
In your viewDidLoad you're erasing the info in name, so everytime you enter your view, it will get deleted.
override func viewDidLoad() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(" ", forKey: "name")
super.viewDidLoad()
}
Also, since name is a UITextField and not a String, when you're saving your name, it should be done like this:
NSUserDefaults.standardUserDefaults().setObject(name.text, forKey:"name")
instead of this:
NSUserDefaults.standardUserDefaults().setObject(name, forKey:"name")
Also, inside your btnSave() method, you're only saving the name.text if it's equal to "". So if you enter any other information, it won't be saved. I'd recommend modifying it so it looks something like this (you could use guard instead of if name.text also):
#IBAction func btnSave() {
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
return
}
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(name.text, forKey:"name")
userDefaults.synchronize()
}
P.S: Remember that NSUserDefaults is intended for user preferences. I'd recommend against saving large amounts of data into NSUserDefaults.
Related
I am building an app (SwiftUI) which will be used for iOS and macOS platform. One of the challenges, I am facing is storing Color (SwiftUI) into the database. In other apps I have used NSValueTransformer to use it with UIColor or NSColor but now the problem is that iOS and macOS share the codebase. I am using Core Data so the data model is being shared but the data saved is on separate SQLite files. If I want to sync the data to iCloud then would't I face issues if iOS app stored color as UIColor and macOS app stored color as NSColor?
How can I resolve this issue?
import UIKit
import CoreData
class saveDataScreenViewController: UIViewController {
#IBOutlet weak var txtName: UITextField!
#IBOutlet weak var txtId: UITextField!
#IBOutlet weak var txtUserName: UITextField!
#IBOutlet weak var txtPassword: UITextField!
#IBOutlet weak var btnSave: UIButton!
//MARK: - Create context for save data
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - save button click with validation and saving data to core data
#IBAction func didTapSave(_ sender: UIButton) {
if txtName.text!.isEmpty {
let alert = UIAlertController(title: "Alert", message: "Please enter Name", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
} else if txtId.text!.isEmpty {
let alert = UIAlertController(title: "Alert", message: "Please enter id", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
} else if txtUserName.text!.isEmpty {
let alert = UIAlertController(title: "Alert", message: "Please enter username", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
} else if txtPassword.text!.isEmpty {
let alert = UIAlertController(title: "Alert", message: "Please enter password", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
} else {
let Entity = NSEntityDescription.insertNewObject(forEntityName: "EntityName", into: context)
clinicEntity.setValue(txtName.text!, forKey: "Name")
clinicEntity.setValue(txtId.text!, forKey: "id")
clinicEntity.setValue(txtPassword.text!, forKey: "password")
clinicEntity.setValue(txtUserName.text!, forKey: "username")
do {
try context.save()
self.txtUserName.text = ""
self.txtPassword.text = ""
self.txtName.text = ""
self.txtId.text = ""
self.navigationController?.popViewController(animated: true)
} catch {
print("Error while insert data!")
}
}
}
}
I've been trying to present a UIAlertController when user entered wrong password for their account, the UIAlertController is located in one separate file inside a Model group which I extend the UIViewController class to add this alert functionality to it. I also has another file inside my model group namely LogIn which I wrote all the logic behind the login process so that I can call it to my LogInVC. However, I got an error of "Attempt to present on whose view is not in the window hierarchy!" whenever the function get call inside my LogInVC. I'm trying to make my project in MVC and I know what caused this error but I just don't know how to fix it. May anyone tell me how to fix this problem?
Alert
import Foundation
import UIKit
extension UIViewController {
//MARK: - Not Enough Information Alert
func notEnoughInfo(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
//MARK: - Incorrect Username and Password
func wrongInfo(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "Try again", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
LogIn
import Foundation
import Firebase
class LogIn: UIViewController{
let db = Firestore.firestore()
//MARK: - userValidation()
func userValidation(Username:String, Password:String){
db.collection("users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapShotDocument = querySnapshot?.documents {
for doc in snapShotDocument {
let data = doc.data()
if let username = data[C.username] as? String, let password = data[C.password] as? String {
if Username == username, Password == password {
print("Log in Successfully")
}
else {
self.wrongInfo(title: "Incorrect password", message: "Try again please")
}
}
}
}
}
}
}
}
LogInVC
import UIKit
import Firebase
class LogInVC: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var logInBtn: UIButton!
let db = Firestore.firestore()
let logIn = LogIn()
override func viewDidLoad() {
super.viewDidLoad()
//logInBtn.layer.cornerRadius = logInBtn.frame.height/5
}
#IBAction func logInBtn(_ sender: UIButton) {
if let username = emailTextField.text, let password = passwordTextField.text{
if username.isEmpty || password.isEmpty{
notEnoughInfo(title: "Not enough information", message: "Please fill in all the necessary information.")
}else{
logIn.userValidation(Username: username, Password: password) //here is where problem occured
//move to another viewcontroller
}
}
}
#IBAction func signUpBtn(_ sender: UIButton) {
let push = storyboard?.instantiateViewController(withIdentifier: C.signUpVC) as! SignUpVC
push.modalPresentationStyle = .fullScreen
present(push, animated: true, completion: nil)
}
} //ends of class
You need to first dismiss the current present alert or present controller. currently you are trying to present controller over a controller that's why it shows this error. Don't present . remove this line from self.wrongInfo(title: "Incorrect password", message: "Try again please") from LogIn.
try this and you can comment again if there is anything regarding this.
I have made an app that recognize's a piece of art/drawing/paper etc using ARKit. When it recognize's the images it switches to another View Controller and tells you about it. It can recognize one piece of art. The question is how can i have the app recognize multiple drawings. Here is the code.
AR View Controller:
import UIKit
import SpriteKit
import ARKit
import AVFoundation
struct ImageInformation {
let name: String
let description: String
let image: UIImage
}
class ViewController: UIViewController, ARSKViewDelegate {
#IBOutlet var sceneView: ARSKView!
#IBAction func options(){
let alert = UIAlertController(title: "Options", message: "Select one of the options below to continue.", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Enable Flashlight", style: .default, handler: { action in
//Enable Flashlight function
func toggleTorch(on: Bool) {
guard let device = AVCaptureDevice.default(for: AVMediaType.video),
device.hasTorch
else { return }
do {
try device.lockForConfiguration()
device.torchMode = on ? .on : .off
device.unlockForConfiguration()
} catch {
//Torch can not be used.
let alert = UIAlertController(title: "Flashlight Error", message: "We are unable to activate the flashlight. This could be because the flashlight is being used by another app", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dissmis", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .destructive, handler: { action in
fatalError("The flashlight could not be used and user restarted app.")
}))
self.present(alert, animated: true)
}
}
toggleTorch(on: true)
}))
//Continue working on this function when you get back!!!
alert.addAction(UIAlertAction(title: "Disable Flashlight", style: .default, handler: { action in
//Enable flashlight function here
func toggleTorch(off: Bool) {
guard let device = AVCaptureDevice.default(for: AVMediaType.video),
device.hasTorch
else { return }
do {
try device.lockForConfiguration()
device.torchMode = off ? .on : .off
device.unlockForConfiguration()
} catch {
//Torch can not be used.
let alert = UIAlertController(title: "Flashlight Error", message: "We are unable to activate the flashlight. This could be because the flashlight is being used by another app", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dissmis", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .destructive, handler: { action in
fatalError("The flashlight could not be used and user restarted app.")
}))
self.present(alert, animated: true)
}
}
toggleTorch(off: false)
}))
alert.addAction(UIAlertAction(title: "Clear Recently Visited Artworks", style: .destructive, handler: { action in
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewController(withIdentifier: "action_done")
self.present(secondViewController, animated: false, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
var selectedImage : ImageInformation?
let images = ["flower" : ImageInformation(name: "Flower Drawing", description: "This is a drawing of a flower and was made by the developer of this app. It was intended to be a thank you card for a teacher on Teacher appreciation day. The Teacher enjoyed the project.", image: UIImage(named: "flower")!)]
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsFPS = false
sceneView.showsNodeCount = false
if let scene = SKScene(fileNamed: "Scene") {
sceneView.presentScene(scene)
}
guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
let alert = UIAlertController(title: "Resources not Found", message: "The files needed for this application to work propely can not be found. What would you like to do about this?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Continue Anyway", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .default, handler: { action in
fatalError("The Recources could not be found on the users device.")
}))
self.present(alert, animated: true)
return
}
let configuration = ARWorldTrackingConfiguration()
configuration.detectionImages = referenceImages
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
// MARK: - ARSKViewDelegate
func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
if let imageAnchor = anchor as? ARImageAnchor,
let referenceImageName = imageAnchor.referenceImage.name,
let scannedImage = self.images[referenceImageName] {
self.selectedImage = scannedImage
self.performSegue(withIdentifier: "switch", sender: self)
return imageSeenMarker()
}
return nil
}
private func imageSeenMarker() -> SKLabelNode {
let labelNode = SKLabelNode(text: "✅")
labelNode.horizontalAlignmentMode = .center
labelNode.verticalAlignmentMode = .center
return labelNode
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "switch"{
if let imageInformationVC = segue.destination as? ImageInformationViewController,
let actualSelectedImage = selectedImage {
imageInformationVC.imageInformation = actualSelectedImage
}
}
}
}
Information View Controller:
import Foundation
import UIKit
class ImageInformationViewController : UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var descriptionText: UILabel!
var imageInformation : ImageInformation?
override func viewDidLoad() {
super.viewDidLoad()
if let actualImageInformation = imageInformation {
self.nameLabel.text = actualImageInformation.name
self.imageView.image = actualImageInformation.image
self.descriptionText.text = actualImageInformation.description
}
}
#IBAction func dismissView(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
How Can i do this?
Thanks!
The function referenceImages(inGroup:bundle:) loads all reference images from your assets. Just put multiple images into your AR Resource Group in your Assets and assign a unique name for all of them.
Currently, my code for NSUserDefaults work fine. The only issue I have now is how can I send the data store to another page? I can get it now save the data, but how do I retrieve that same data via NSUserDefaults to a secondary page?
import UIKit
class MainTableViewController: UITableViewController, UITextFieldDelegate {
let userDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var name: UITextField!
#IBAction func btnSave() {
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
else {
userDefaults.setObject(name.text, forKey:"name")
userDefaults.synchronize()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
name.text = userDefaults.stringForKey("name")
}
}
You can retrieve your data from your second page with same code.
Create your NSUserDefaults in second page like this ;
First of all add these codes to your FirstPage in IBAction func. Don't use the if else statement for test.
UserDefaults.standard.set("yourValue", forKey: "yourKey")
When you press the button it should be save your data with your key.
In the secondPage you can retrieve your data in your viewDidLoad method like this;
override func viewDidLoad() {
super.viewDidLoad()
print(UserDefaults.standard.value(forKey: "yourKey") ?? "defaultValue in case key is not found")
print(value)
}
It should be print your value to your console (bottom window in your xcode) when the secondPage is open. If you still can't retrieve your value please share to me a your storyboard picture. I want to see your connections.
I'm trying to create an app with Swift and Parse. I'm using Xcode 7 and Swift 2.
I want to show a alert message when User is login failed, here is my function:
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
But I've got this error when run in the emulator:
2015-10-02 11:32:39.988 RedString[2089:886501] Warning: Attempt to present <UIAlertController: 0x7a934400> on <MyProject.ViewController: 0x7b985150> whose view is not in the window hierarchy!
Login failed.........!
I've googled about it, but I didn't found the clear solution.
Here is my whole class:
import UIKit
import Parse
import ParseUI
class ViewController: UIViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
self.setupLoginView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func logInViewController(logInController: PFLogInViewController, didLogInUser user: PFUser){
self.dismissViewControllerAnimated(true, completion: nil)
}
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser){
self.dismissViewControllerAnimated(true, completion: nil)
}
func signUpViewController(signUpController: PFSignUpViewController, didFailToSignUpWithError error: NSError?){
print("Sign up failed.........!")
}
func setupLoginView(){
if(PFUser.currentUser() == nil){
let loginViewController = PFLogInViewController()
loginViewController.delegate = self
let signUpViewController = PFSignUpViewController()
signUpViewController.delegate = self
loginViewController.logInView!.logo = UIImageView(image: UIImage(named:"logo.png"))
loginViewController.signUpController = signUpViewController
self.presentViewController(loginViewController, animated: true, completion: nil)
}else{
print("login as: " + PFUser.currentUser()!.username!)
//prepare new view here
}
}
}
when
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
is called, your view controller is not on screen. Therefore you could either
dismiss the logInViewController, then display the alert and pass
setUpLogInView as the handler.
or in the above function, try logInController.presentViewController
instead of self.presentViewController
You have to call presentViewController:animated:completion on your login view controller.
Here is a simplified version of your class to show you what I mean:
class ViewController: UIViewController {
weak var loginViewController: UIViewController?
func setupLoginViewController() {
let loginVC = PFLogInViewController()
// setup loginVC
loginViewController = loginVC // store the reference
}
func loginDidFail() {
let alertVC = UIAlertController(...)
// setup alertVC
loginViewController?.presentViewController(...) // present the alert from the login view controller
}
}
I had the same problem in Objective-C, it occurs when the parent window has not yet been drawn to the screen. I solved the problem by calling the code from within viewDidAppear rather than viewDidLoad. See UIAlert error, whose view is not in the window hierarchy