Update NSTextField based on selection in NSPopupButton - swift

I've only been coding for a few weeks so forgive my no-doubt clumsy code.
I have a NSTextField that needs to update based upon a selection from an NSPopupButton
These are my outlets:
#IBOutlet weak var inputText: NSTextField!
#IBOutlet weak var outputText: NSTextField!
#IBOutlet weak var inputBrowseButton: NSButton!
#IBOutlet weak var outputBrowseButton: NSButton!
#IBOutlet weak var codecSelector: NSPopUpButton!
These are my variables:
// derive default path and filename from #IBAction of inputBrowseButton
var defaultUrl: URL!
// derived from default of inputBrowse unless outputBrowse is used
var outputUrl: URL!
// change output path from #IBAction of outputBrowseButton
var outputDirectory: URL!
// change path extension from #IBAction of codecSelector NSPopupButton
var codecChosen: String = ""
// dictionary of arguments to form script for ffmpeg
var ffmpegCodecArg: String = ""
And here is my method to pull all that together:
// construct output path for use in ffmpegArguments dictionary and display in outputText NSTextField
func updateOutputText(defaultUrl: URL?, outputDirectory: URL?) -> String {
if defaultUrl != nil && outputDirectory != nil {
let outputFile = defaultUrl!.deletingPathExtension()
let outputFilename = outputFile.lastPathComponent
outputUrl = outputDirectory!.appendingPathComponent(outputFilename)
outputText.stringValue = outputUrl.path + "\(codecChosen)"
} else if defaultUrl == nil && outputDirectory != nil {
outputText.stringValue = outputDirectory!.path
} else {
outputUrl = defaultUrl!.deletingPathExtension()
outputText.stringValue = outputUrl.path + "\(codecChosen)"
}
return outputText.stringValue
}
Now at the present this function isn't working because I haven't figured out how to call it yet. But that's an issue for another time, not what I'm asking about here.
Previously I was running
outputUrl = defaultUrl!.deletingPathExtension()
outputText.stringValue = outputUrl.path + "\(codecChosen)"
as part of my inputBrowseButton #IBAction method, and I was running
let outputFile = defaultUrl!.deletingPathExtension()
let outputFilename = outputFile.lastPathComponent
outputUrl = outputDirectory!.appendingPathComponent(outputFilename)
outputText.stringValue = outputUrl.path + "\(codecChosen)"
as part of my outputBrowseButton #IBAction method.
Which worked fine, EXCEPT when it came to updating outputText.stringValue when the codecChosen variable was assigned a new value in my #IBAction for the codecSelector NSPopupButton method.
I suspect the problem is that I don't have my outputText NSTextField set up to update when the codecSelector NSPopupButton changes. What do I need to do to make that happen?

How do you set the text in an NSTextField?
Which worked fine, EXCEPT when it came to updating outputText.stringValue when the codecChosen variable was assigned a new value in my #IBAction for the codecSelector NSPopupButton method.
Try using a separate method to change the text.
func changeText(inputString: String, inputField: NSTextField) {
inputField.stringValue = inputString
}
Separating your code more might make it easier for you to keep track of while you are starting out. I am not entirely familiar with macOS, so that method may not work, but try working with the principle of it and see if you can progress.

Related

How to access a text field value and convert to double

I am working on trying to build a tip calculator using swift but am running into a multitude of problems. I have a text box where the user enters in the bill amount and then they can adjust a slider to indicate the percentage that they want to tip. I have managed to get the slider and label associated with it to work where changing the value on the slider changes the label. However, I can't figure out how to get the text field to work. I attempted to create an action for the bill amount text box being changed but no matter what I put in it, it doesn't seem like the code is ever being executed (Whenever I run the code and click in the text box, the keyboard won't go away so maybe this is part of the problem?). All that I need is to be able to access the value in the text box field inside of my action when I click the calculate button but I can't seem to even get the value to show up much less convert it to a double so I can perform calculations on it. I am super new to swift and really want to learn what I am doing wrong. I have tried multiple tutorials and similar questions on here but none of them work. I appreciate all help y'all can give.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtBillAmount: UITextField!
#IBOutlet weak var lblTipPercentage: UILabel!
#IBOutlet weak var sldTipPercentage: UISlider!
#IBOutlet weak var lblTipAmount: UILabel!
#IBOutlet weak var lblTotalAmount: UILabel!
var tipPercentage = 10
var billAmount = ""
#IBAction func valueChanged(sender: AnyObject) {
let currentValue = Int(sldTipPercentage.value)
lblTipPercentage.text = "\(currentValue)%"
tipPercentage = currentValue
}
#IBAction func btnCalculate(sender: AnyObject) {
lblTipAmount.text = "\(billAmount)"
}
#IBAction func txtBillAmountValueChanged(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
//txtBillAmount.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnReset(sender: UIButton) {
sldTipPercentage.value = 10
lblTipPercentage.text = "10%"
txtBillAmount.text = ""
lblTipAmount.text = "--"
lblTotalAmount.text = "--"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is a screenshot of how the UI looks
Error that I am getting
[Xcode >= 7 and Swift > 2]
Try like this
#IBAction func btnCalculate(sender: AnyObject) {
if let textValue = txtBillAmount.text {
let billAmnt = Double(textValue)
let tipAmount = (billAmnt*tipPercentage)/100
lblTipAmount.text = "\(tipAmount)"
}
}
If you are using Xcode-6 and Swift < 2 then try the following way. because String initializer for Double is only available in Swift 2 (Xcode 7). [Recommend update your Xcode]
#IBAction func btnCalculate(sender: AnyObject) {
let billAmnt = (txtBillAmount.text! as NSString).doubleValue
let tipAmount = (billAmnt*tipPercentage)/100
lblTipAmount.text = "\(tipAmount)"
}

Would using Swift's Guard or if Let Keywords take care of not having to use this checkNil function?

I understand that programming in a language such as Swift, intent can be expressed in numerous ways. I'm following a tutorial and saw the code below which isn't what I'm used to seeing because the author created a function to check for nil. Could the checkNil function be avoided by simply using guard or if let statements or does improve the code somehow? It's a great tutorial and I'm simply looking to improve my knowledge of interpreting other developer's code to find ways to be more concise myself.
import UIKit
class ViewController: UIViewController {
private let rideService = DummyRideService()
private var rides = [Ride]()
#IBOutlet var from: UITextField!
#IBOutlet var to: UITextField!
#IBOutlet var ridesTableView: UITableView!
#IBAction func findRouteButtonClicked(){
let fromText = self.checkNil(from.text as AnyObject?)
let toText = self.checkNil(to.text as AnyObject?)
}
func checkNil(_ string: AnyObject?) -> String {
return string == nil ? "": string as! String
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
there are many ways to get rid of the entire checkNil(_:) nonsense quickly.
idea #1
I'd just simply make the outlets optional weak properties then use optional chaining, like:
#IBOutlet private weak var from: UITextField?
#IBOutlet private weak var to: UITextField?
then you could use this:
#IBAction func findRouteButtonClicked() {
let fromText = from?.text ?? ""
let toText = to?.text ?? ""
}
idea #2
or if you want to make it more elegant, you could create an extension on UITextField, like:
extension UITextField {
var alwaysText: String {
return self.text ?? ""
}
}
so you could replace the let lines with these more readable ones, like
#IBAction func findRouteButtonClicked() {
let fromText = from.alwaysText
let toText = to.alwaysText
}
You can use optional binding (under the optionals section) to check if the value is nil in this code:
let fromText = self.checkNil(from.text as AnyObject?)
can be changed to
if let fromText = from.text {
// Run bit of code
}
or as you aren't using that value straight away, you can do:
let fromText = from.text ?? ""
using the coalescing operator which is equivalent to using that check-for-nil function.
Functions known for usage to gain re usability , he constructs it once and uses it's code twice inside findRouteButtonClicked of course you can use guard or let statements but what if there are many strings to check their nullability in different parts of the VC

Preloaded database with Realm Swift

I'm having a little trouble in getting PreLoaded data to work. At first, here is code:
if firstRun {
userDefaults.set(1, forKey: "dayCount")
userDefaults.set(dateFormatter.string(from: date), forKey: "date")
let newPath = defaultPath.deletingLastPathComponent()
let v0Path = bundleURL("default")
do {
//try FileManager.default.removeItem(at: defaultPath)
try FileManager.default.copyItem(at: v0Path!, to:defaultPath)
} catch {
print(error)
}
} ...
v0Path:
file:///Users/dimasalbuquerque/Library/Developer/CoreSimulator/Devices/7FE635BA-AA7A-4241-AF3B-88AD60693AE7/data/Containers/Bundle/Application/B6739B92-B7D8-4DD8-9DA8-CD9BBD84B109/Example.app/default.realm
defaultPath:
file:///Users/dimasalbuquerque/Library/Developer/CoreSimulator/Devices/7FE635BA-AA7A-4241-AF3B-88AD60693AE7/data/Containers/Data/Application/A86C4337-9006-497C-A688-AD781F49EF04/Documents/default.realm
I followed this guide: https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift-2.2/Migration/AppDelegate.swift
Problem is that when the program is running by the first time, it executes the code correctly, but when I try to access realm database, it says it's empty. Although when I open the app for the second time, it works, the data is there. It's already over 1 week that I'm trying to solve this, I've searched through all the net but without success.
Here is where Realm is first called:
class HomeViewController: UIViewController {
#IBOutlet weak var message: UILabel!
#IBOutlet weak var backward: UIButton!
#IBOutlet weak var forward: UIButton!
#IBOutlet weak var background: UIImageView!
#IBOutlet weak var timeBtn: UIButton!
#IBOutlet weak var favoriteBtn: UIButton!
#IBOutlet weak var googleAd: GADBannerView!
let userDefaults = UserDefaults.standard
let realm = try! Realm()
var currentDate = 1
var time = 0 {
didSet {
if time != oldValue {
randomBackground(time)
}
}
}
var dailyMessage: DailyMessagesRealm?
var currentMsg: Message?
override func viewDidLoad() {
super.viewDidLoad()
let first = userDefaults.bool(forKey: "notFirstRun")
if !first {
userDefaults.set(true, forKey: "notFirstRun")
reNew()
}
let day = userDefaults.integer(forKey: "dayCount")
currentDate = day
let empty = realm.objects(DailyMessagesRealm.self).isEmpty
let dailyMessage = realm.objects(DailyMessagesRealm.self).filter("date == '\(day)'").first
//*********Error occurs here***********
self.dailyMessage = dailyMessage!
self.currentMsg = dailyMessage?.morningMessage
self.currentMsg = dailyMessage?.morningMessage
changeMessage((dailyMessage?.morningMessage?.message)!)
initAds()
changeBackground("morning1")
checkFavorite()
} ...
From the sound of it, you must be calling Realm() somewhere before you're performing your copy operation here.
default.realm is only opened when you call Realm() for the first time. Once it's open though, it stores cached information about the file in memory, so if you replace it after the fact, you'll end up with unpredictable behavior.
If you absolutely need to do some kind of operation with Realm(), you can enclose it in an #autoreleasepool { } block to ensure its cached entries in memory are predictably flushed before you do the file copy.
Other than that, I recommended checking your code to ensure you're performing this file copy before touching any instances of Realm() pointing at it.

How do I change the value of a variable in a different file?

I am trying to read and write a variable to display in a note, and I can read it and it will display, but when I try and save what the user typed to that variable in the different file it will not save.
Here is the variables I am trying to read and write in the Project file.
class Project
{
var title = ""
var content = ""
var after = " "
}
And here is where I am trying to do the reading and writing.
class NoteDetailViewController: UIViewController
{
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var contentTextField: UITextView!
#IBOutlet weak var afterTextField: UITextView!
var project = Project()
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
titleTextField.text = project.title
contentTextField.text = project.content
afterTextField.text = project.after
}
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
project.title = titleTextField.text!
project.content = contentTextField.text!
project.after = afterTextField.text!
}
}
I know the text field outlets aren't the problem because they show whatever value is originally in the variables, I simply can't save a new value to those variables from here.
try this for dynamic global variable
class Project {
static let sharedInstance = Project()
var title = ""
var content = ""
var after = " "
}
But I need to save and retrieve data as below
// save title
Project.sharedInstance.title = "My Title"
// call / print title
print(Project.sharedInstance.title)
also take note this global variable is accessible from any classes

setting the countdown interval of a WatchKit timer

New to watch development however....
My app gets the user to select a duration for a countdown timer using a slider on one interface controller as shown below:
class game_settings: WKInterfaceController {
#IBOutlet weak var halflength: WKInterfaceSlider!
#IBOutlet weak var halflengthdisplay: WKInterfaceLabel!
#IBOutlet var sethalflengthbutton: WKInterfaceButton!
#IBAction func halfsliderdidchange(value: Float) {
halflengthdisplay.setText("\(value)")
}
override func contextForSegueWithIdentifier(initialhalftogame: String) -> AnyObject? {
// You may want to set the context's identifier in Interface Builder and check it here to make sure you're returning data at the proper times
// Return data to be accessed in ResultsController
return self.halflengthdisplay
}
}
i got this from the following question: Passing data question
then i want the selected interval to be used for the timer on another interface controller as shown below.
class main_game_controller: WKInterfaceController {
#IBOutlet weak var WKTimer: WKInterfaceTimer!//timer that the user will see
var internaltimer : NSTimer?
var ispaused = false
var elapsedTime : NSTimeInterval = 0.0
var StartTime = NSDate()
#IBOutlet var extratime_button: WKInterfaceButton!
#IBOutlet var endgame_button: WKInterfaceButton!
#IBOutlet var sanction_button: WKInterfaceButton!
#IBOutlet var goal_button: WKInterfaceButton!
#IBOutlet var additional_time_timer: WKInterfaceTimer!
#IBOutlet var reset_timer_button: WKInterfaceButton!
#IBOutlet var stop_timer_button: WKInterfaceButton!
#IBOutlet var Start_timer_button: WKInterfaceButton!
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
var halflengthinterval : NSTimeInterval// variable was written to, but never read
// Configure interface objects here.
if let halflength: String = context as? String {
halflengthinterval = Double(halflength)!
}
}
override func willActivate() {
super.willActivate()
}
#IBAction func Start_button_pressed() {
internaltimer = NSTimer.scheduledTimerWithTimeInterval(halflengthinterval, target:self, selector: Selector("timerdone"), userInfo: nil, repeats:false) //use of unfesolved identifier"halflengthinterval"
WKTimer.setDate(NSDate(timeIntervalSinceNow: halflengthinterval))
WKTimer.start()//use of unresolved identifier "halflengthinterval"
}
#IBAction func stop_timer_button_pressed() {
if ispaused{
ispaused = false
internaltimer = NSTimer.scheduledTimerWithTimeInterval(halflengthinterval - elapsedTime, target: self, selector: Selector("timerDone"), userInfo: nil, repeats: false)//use of unresolved identifier 'halflengthinterval'
WKTimer.setDate(NSDate(timeIntervalSinceNow: halflengthinterval - elapsedTime))//use of unresolved identifier 'halflengthinterval'
WKTimer.start()
StartTime = NSDate()
stop_timer_button.setTitle("Pause")
}
else{
ispaused = true
//get how much time has passed before they paused it
let paused = NSDate()
elapsedTime += paused.timeIntervalSinceDate(StartTime)
//stop watchkit timer on screen
WKTimer.stop()
//stop internal timer
internaltimer!.invalidate()
//ui modification
stop_timer_button.setTitle("Resume")
}
}
I was following the answer provided in this question: WKInterface implementation
as you can see in the commented lines above, I'm receiving several errors associated with the variable halflengthinterval. I get the feeling that I'm not correctly passing the interval value between the two interface controllers, but for the life of me i have no idea how to do it.
Could someone please help me in showing
how to pass the value for the timer from the first interface
controller to the second interface controller and
how to correctly set the countdown timer for the length of time selected by the slider in the first interface controller.
Thanks very much!
Let's fix first the error regarding to NSInterval, NSInterval is just a typealis for the type Double:
typealias NSTimeInterval = Double
So the problem you're facing is how to convert a String to a Double and the way is using the Double constructor like in this way:
Double(IntValue)
Regarding how to pass data from two WKInterfaceController you're doing in the right way, but you have one mistake to fix. If you want to pass data from one WKInterfaceController to another WKInterfaceController using segues you can use the contextForSegueWithIdentifier, but in your case you are returning a NSInterval type or Double and then you're trying to cast as an String and this fail in this line of code always:
// Configure interface objects here.
if let halflength: String = context as? String {
halflengthinterval = Double(halflength)!
}
You have to change it to this line instead using the guard statement if you like or using optional-binding, it's up to you:
guard
guard let halflength = context as? Double else {
return
}
self.halflengthinterval = Double(halflength)
optional-binding
if let halflength = context as? Double {
self.halflengthinterval = Double(halflength)
}
I hope this help you.