I have successfully implemented core data and UISteppers. Every time I try to edit a saved record the UI Stepper starts over from 0. Please help me to figure put what additional code I need to retain the already edited value.
// This function adds the stepper to a field
//issue: it does not remember the score when i edit it and starts over
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text = Int(sender.value).description
}
#IBAction func pointStepperPressed(_ sender: UIStepper) {
pointTF.text = Int(sender.value).description
}
#IBAction func savingsStepperPressed(_ sender: UIStepper) {
savingsTF.text = Int(sender.value).description
}
}
I have linked core data like so:
import CoreData
class AktieViewController: UIViewController {
#IBOutlet weak var counterStepper: UIStepper!
#IBOutlet weak var pointsStepper: UIStepper!
#IBOutlet weak var savingsStepper: UIStepper!
var selectedAktie: Aktie? = nil
override func viewDidLoad()
{
super.viewDidLoad()
if(selectedAktie != nil) {
savingsTF.text = selectedAktie?.saving
counterTF.text = selectedAktie?.counter
pointTF.text = selectedAktie?.point
}
}
#IBAction func saveAction(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
if(selectedAktie == nil)
{
let entity = NSEntityDescription.entity(forEntityName: "Aktie", in: context)
let newAktie = Aktie (entity: entity!, insertInto: context)
newAktie.saving = savingsTF.text
newAktie.point = pointTF.text
newAktie.counter = counterTF.text
do {
try context.save()
aktieList.append(newAktie)
navigationController?.popViewController(animated: true)
}
catch
{
print("context save error")
}
}
I also have an edit and delete function.
This function eventually solved my question:
#IBAction func counterStepperPressed(_ sender: UIStepper) {
let initialValue=Int(counterTF.text) ?? 0
let newValue=Int(sender.value)+initialValue
counterTF.text="\(newValue)"
}
I have managed to add the following code to remember the value in the stepper.
if let value=UserDefaults.standard.value(forKey: "counterStepper") as? Double {
counterStepper.value=value counterTF.text=String(describing: value)
And in the action I have added the following code.
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text=String(describing: sender.value)
UserDefaults.standard.setValue(sender.value, forKey: "counterStepper")
NotificationCenter.default.post(Notification.init(name: Notification.Name("StepperDidChangeValue")))
}
The only issue I have is that if I edit a second item it remembers the value of the first item. Somehow it is not remembering the original value of the item.
Related
Trying to save the user's textView.text into Realm. Figured if I call my save function into the textFieldDidEndEditing function, it would trigger my Realm database to save it. Function runs ok (prints "Saved Successfully" when I end editing), but when I close out and come back, none of the data is there.
import UIKit
import RealmSwift
class NoteViewController: UIViewController, UITextViewDelegate {
var textView = UITextView()
var notes: Results<Notes>?
let realm = try! Realm()
var selectedNote: Menu? {
didSet {
loadNotes()
}
}
#IBOutlet weak var theTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
loadNotes()
self.textView.delegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func viewDidDisappear(_ animated: Bool) {
saveNote()
}
func textViewDidBeginEditing(_ textView: UITextView) {
}
func textViewDidEndEditing(_ textView: UITextView) {
saveNote()
}
//MARK: - Data Manipulation
func loadNotes() {
notes = realm.objects(Notes.self)
textView.reloadInputViews()
}
func saveNote() {
if let currentNote = self.selectedNote {
do {
try self.realm.write {
let newNote = Notes()
newNote.body = theTextView.text!
newNote.dateCreated = Date()
currentNote.notes.append(newNote)
print("Saved successfully")
}
} catch {
print("Error saving note body, with \(error)")
}
}
}
}
Realm File:
class Notes: Object {
#objc dynamic var body: String = ""
#objc dynamic var dateCreated: Date?
var parent = LinkingObjects(fromType: Menu.self, property: "notes")
}
Menu Realm File:
class Menu: Object {
#objc dynamic var name: String = ""
#objc dynamic var preview: String = ""
let notes = List<Notes>()
}
'''
It looks like you are using a storyboard to create your layout.
Get rid of var textView = UITextView() and textView.reloadInputViews() then.
Replace self.textView.delegate = self with theTextView.delegate = self in viewDidLoad().
To check the content of theTextView modify your print statement:
print("Saved successfully: \(theTextView.text!)")
If there is no text printed right after "Saved successfully: " then check Interface builder for theTextView: verify the outlet of theTextView and that this is actually the UITextView you are using (since your app does print something it looks like it is hooked up somehow because otherwise it would crash in newNote.body = theTextView.text!).
I think you also forgot to actually initialise theTextView with notes.body.
Try to modify loadNotes() like this:
func loadNotes() {
notes = realm.objects(Notes.self)
theTextView.text = notes.body
}
I have seen some other questions asked but I am having trouble getting it to work. I have a Mac app coded in swift and it has a Firebase login but the user types a key in that is stored on Firebase, is there a way to automatically delete that key when the user has successfully used it?
This is my database.
This is the code that is used currently.
import Cocoa
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: NSViewController {
#IBOutlet weak var textUsername: NSTextField!
#IBOutlet weak var textPassword: NSSecureTextFieldCell!
#IBOutlet weak var btnLogin: NSButton!
var keyArray = \[Int64\]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear() {
}
func getLoginState() -> Bool{
let state = UserDefaults.standard.bool(forKey: "isRegistered")
if (state) {
return true
} else {
return false
}
}
override func viewDidAppear() {
let state = self.getLoginState()
if (state){
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
}
var ref: DatabaseReference!
ref = Database.database().reference()
let keyRef = ref.child("key1")
keyRef.observe(DataEventType.childAdded, with: { (snapshot) in
// let postDict = snapshot.value as? \[String : AnyObject\] ?? \[:\]
let keyStr = snapshot.value as? Int64
if let actualPost = keyStr{
self.keyArray.append(actualPost)
}
})
}
#IBAction override func dismissViewController(_ viewController: NSViewController) {
dismiss(self)
}
#IBAction func close(sender: AnyObject) {
self.view.window?.close()
}
#IBAction func onSignup(_ sender: Any) {
// self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "gotosignup"), sender: sender)
// self.view.window?.close()
}
func dialogOK(question: String, text: String) -> Void {
let alert: NSAlert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = NSAlert.Style.warning
alert.addButton(withTitle: "OK")
alert.runModal()
}
#IBAction func onLogin(_ sender: Any) {
//self.btnLogin.isEnabled = false
var isKey = false
if (!self.textUsername.stringValue.isEmpty) {
for key in keyArray{
if(Int64(self.textUsername.stringValue)! == key)
{
UserDefaults.standard.set(true, forKey:"isRegistered")
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
isKey = true
}
}
if (!isKey){
self.dialogOK(question: "Error", text: "Invalid Key")
}
} else {
self.dialogOK(question: "Error", text: "Please Input Key")
}
}
}
You can't sort your database like that and expect a working code, even if there's any. It will make a messy code:
You need to:
Sort your database like [1220:0]. the key first. 0 & 1 as an indicator if it's used or not.
Once the user taps onLogin() you need to set the used key value to 1
Setup Cloud Functions to check if the used key is equal to 1, if yes. then remove the key.
Do the rest of the work.
Related Articles to get you started:
Extend Realtime Database with Cloud Functions
functions.database.RefBuilder
Im using RxSwift and RxCocoa in my project.
I have some UITextField named "lastNameTF", and there is a UILabel name "lastNameTitle".
I wanna know if there is any way to set the isHidden value of lastNameTitle always be equal to isHidden value of lastNameTF using RxSwift.
I believe you can use KVO as described here -
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#kvo
It is super easy to use KVO. Here is an example of exactly what you are trying to do, just without using RxSwift (don't know what that is...)
Here is the gist of it
class ViewController: UIViewController {
private var lastNameTextFieldHiddenContext = 0
private var lastNameObservingView:UIView? = nil
#IBOutlet weak var lastNameLabel: UILabel!
#IBOutlet weak var lastNameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// add the observer
lastNameTextField.addObserver(
self,
forKeyPath: "hidden",
options: [.new],
context: &self.lastNameTextFieldHiddenContext
)
}
/// function will be called whenever an added observer is triggered
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?
) {
// make sure it is our text field isHidden observer
if context == &self.lastNameTextFieldHiddenContext {
// get the new value that was set
if let newValue = change?[NSKeyValueChangeKey.newKey] as? Bool {
// do what needs to be done when the observer is triggered
self.lastNameLabel.isHidden = newValue
}
}
}
deinit {
// remove the observer
if let view = self.lastNameObservingView {
view.removeObserver(self, forKeyPath: "hidden")
self.lastNameObservingView = nil
}
}
#IBAction func showHideButtonAction(_ sender: Any) {
self.lastNameTextField.isHidden = !self.lastNameTextField.isHidden
}
}
If you still need a simple RxSwift approach please try this:
// Controls are visible by default (isHidden = false)
let isControlHidden = BehaviorRelay<Bool>(value: false)
override func viewDidLoad() {
super.viewDidLoad()
let isHiddenDriver = self.isControlHidden.asDriver()
isHiddenDriver
.drive(self.lastNameTitle.rx.isHidden)
.disposed(by: disposeBag)
isHiddenDriver
.drive(self.lastNameTF.rx.isHidden)
.disposed(by: disposeBag)
}
Since you need both control visibilities bound to each other, you can use a Subject or Relay to achieve that, in this case isControlHidden. So, if you want to show/hide the, you just emit a new signal:
#IBAction func hide(_ sender: Any) {
self.isControlHidden.accept(true)
}
#IBAction func show(_ sender: Any) {
self.isControlHidden.accept(false)
}
I am new here and would like to ask a question that has been working for me for days. I'm just learning Swift 4 and I've come quite a long way. I really do not know what to do any more, and my books on swift do not help me either.
I have created a small testapp, in which should simply be charged.
There are 5 view controllers. The first one has 4 buttons to get to one of the other 4 and to enter a number there in a text box. This number is then output in the first viewcontroller in a label. The numbers are displayed and even the last entered number is displayed again after a restart of the app.
But now I want to charge off the numbers in the first viewcontroller. How can I fix the code?
My Viewports:
my viewports
code from main viewport:
import UIKit
class ViewController: UIViewController, sendValue1, sendValue2, sendValue3, sendValue4 {
#IBOutlet weak var value1: UILabel!
#IBOutlet weak var value2: UILabel!
#IBOutlet weak var value3: UILabel!
#IBOutlet weak var value4: UILabel!
#IBOutlet weak var calculatedValue1: UILabel! // here i want to see the calculated value like from the label 1-4...value1 + value2 + value3 + value4 = ???
#IBOutlet weak var calculatedValue2: UILabel! // here the same like in claculatedValue1 value but with "-" or "*" or something else...
func value1Data(data: String) {
value1.text = data
UserDefaults.standard.set(value1.text, forKey: "value1")
}
func value2Data(data: String) {
value2.text = data
UserDefaults.standard.set(value2.text, forKey: "value2")
}
func value3Data(data: String) {
value3.text = data
UserDefaults.standard.set(value3.text, forKey: "value3")
}
func value4Data(data: String) {
value4.text = data
UserDefaults.standard.set(value4.text, forKey: "value4")
}
override func viewDidAppear(_ animated: Bool) {
if let lastValue1Data = UserDefaults.standard.object(forKey: "value1") as? String {
value1.text = lastValue1Data
}
if let lastValue2Data = UserDefaults.standard.object(forKey: "value2") as? String {
value2.text = lastValue2Data
}
if let lastValue3Data = UserDefaults.standard.object(forKey: "value3") as? String {
value3.text = lastValue3Data
}
if let LastValue4Data = UserDefaults.standard.object(forKey: "value4") as? String {
value4.text = LastValue4Data
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "VC1" {
let SendingVC1: Value1ViewController = segue.destination as! Value1ViewController
SendingVC1.delegate = self
}
if segue.identifier == "VC2" {
let SendingVC2: Value2ViewController = segue.destination as! Value2ViewController
SendingVC2.delegate = self
}
if segue.identifier == "VC3" {
let SendingVC3: Value3ViewController = segue.destination as! Value3ViewController
SendingVC3.delegate = self
}
if segue.identifier == "VC4" {
let SendingVC4: Value4ViewController = segue.destination as! Value4ViewController
SendingVC4.delegate = self
}
}
#IBAction func unwindToView1(_ segue: UIStoryboardSegue) {
}
and the code from one of the other four:
import UIKit
protocol sendValue1 {
func value1Data(data: String)
}
class Value1ViewController: UIViewController {
var delegate: sendValue1? = nil
#IBOutlet weak var textValue1: UITextField!
#IBAction func done(_ sender: Any) {
if delegate != nil {
if textValue1.text != nil {
let data = textValue1.text
delegate?.value1Data(data: data!)
dismiss(animated: true, completion: nil)
}
}
}
why is the result always nil here?
let a = Float(value3.text!) ?? 0
let b = Float(value4.text!) ?? 0
let SUM = a + b
calculatedValue1.text = "\(SUM)" + "m"
No matter what I do, the numbers are not processed ...
I am really struggling to pass the contents of one array from a view controller to another to set up the contents of a nscombobox. I have tried everything I can think of, prepare for segue, init; but nothing seems to work.
the program flow is as follows: the user enter a number into a text field and based on it an array with the size of the number is created. Once the user presses a button the next VC appears that has a combo box and inside that combo box those numbers need to appear. All my attempts result in an empty array being passed. Could someone please take a bit of time and help me out. Im sure I'm doing a silly mistake but cannot figure out what.
Code listing below:
Class that take the user input. At this stage I'm trying to pass the contents of the array in the next class as I gave up on prepare for segue because that one crashes because of nil error. Please note that prepare for segue is uncommented in the code listing just for formatting purposes here. Im my program it is commented out as I am using perform segue at the moment.
Any solution would be nice please. Thank you.
import Cocoa
class SetNumberOfFloorsVC: NSViewController {
//MARK: - Properties
#IBOutlet internal weak var declaredNumber: NSTextField!
internal var declaredFloorsArray = [String]()
private var floorValue: Int {
get {
return Int(declaredNumber.stringValue)!
}
}
//MARK: - Actions
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction private func setNumberOfFloors(_ sender: NSButton) {
if declaredNumber.stringValue.isEmpty {
let screenAlert = NSAlert.init()
screenAlert.messageText = "Please specify the number of floors!"
screenAlert.addButton(withTitle: "Got it!")
screenAlert.runModal()
} else if floorValue == 0 || floorValue < 0 {
let screenAlert = NSAlert.init()
screenAlert.messageText = "Please input a correct number of floors!"
screenAlert.addButton(withTitle: "Got it!")
screenAlert.runModal()
} else {
for i in 0...floorValue - 1 {
declaredFloorsArray.append(String(i))
}
print("\(declaredFloorsArray)")
let declareNumberOfRoomsVC = SetNumberOfRoomsForFloorVC(boxData: declaredFloorsArray)
declareNumberOfRoomsVC.boxData = declaredFloorsArray
performSegue(withIdentifier: "set number of rooms", sender: self)
}
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if segue.identifier == "set number of rooms" {
if let addRoomsVC = segue.destinationController as? SetNumberOfRoomsForFloorVC {
addRoomsVC.floorBox.addItems(withObjectValues: declaredFloorsArray)
}
}
}
}
this is the class for the next VC with the combo box:
import Cocoa
class SetNumberOfRoomsForFloorVC: NSViewController, NSComboBoxDelegate, NSComboBoxDataSource {
//MARK: - Properties
#IBOutlet internal weak var floorBox: NSComboBox!
#IBOutlet private weak var numberOfRoomsTxtField: NSTextField!
internal var boxData = [String]()
//MARK: - Init
convenience init(boxData: [String]) {
self.init()
self.boxData = boxData
}
//MARK: - Actions
override func viewDidLoad() {
super.viewDidLoad()
floorBox.usesDataSource = true
floorBox.dataSource = self
floorBox.delegate = self
print("\(boxData)")
}
#IBAction private func setRoomsForFloor(_ sender: NSButton) {
}
//MARK: - Delegates
func numberOfItems(in comboBox: NSComboBox) -> Int {
return boxData.count
}
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return boxData[index]
}
}
First you should remove the following code.
let declareNumberOfRoomsVC = SetNumberOfRoomsForFloorVC(boxData: declaredFloorsArray)
declareNumberOfRoomsVC.boxData = declaredFloorsArray
I assume you think that the viewController you created here is passed to prepareForSegue. However the storyboard instantiates a new viewController for you.
After that you need to set your declaredFloorsArray as the the boxData of the new viewController in prepareForSegue and you should be good to go.