Bind properties in Swift to NSTextFields

流过昼夜 提交于 2019-12-04 06:26:08

问题


I'm working on a unit converter written in Swift that will automatically display the updated units within the appropriate NSTextFields. For this example, if the user inputs minutes into the minutesField, the secondsField and hoursField should automatically update to display the converted values from the properties. Below is an example of the code in my view controller:

import Cocoa

class ViewController: NSViewController {

    @IBOutlet weak var secondsField: NSTextField!
    @IBOutlet weak var minutesField: NSTextField!
    @IBOutlet weak var hoursField: NSTextField!

    var sec: Double = 1
    var min: Double = 1
    var hr: Double = 1

    let conv = [[1,     0.0166666666666667, 0.000277777777777778],
                [60,    1,                  0.0166666666666667],
                [3600,  60,                 1]]

    func updateTextFields() {
        secondsField.stringValue = "\(sec)"
        minutesField.stringValue = "\(min)"
        hoursField.stringValue = "\(hr)"
    }
}

extension ViewController: NSTextFieldDelegate {

    override func controlTextDidChange(obj: NSNotification) {

        let tag = obj.object?.tag() ?? 0
        let text = obj.object?.stringValue ?? "1"
        let value = Double(text) ?? 1.0

        switch tag {
        case 0:
            // base unit = seconds
            sec = value
            min = value * conv[0][1]
            hr = value * conv[0][2]
        case 1:
            // base unit = minutes
            sec = value * conv[1][0]
            min = value
            hr = value * conv[1][2]
        case 2:
            // base unit = hours
            sec = value * conv[2][0]
            min = value * conv[2][1]
            hr = value
        default:
            "none"
        }
        updateTextFields()
    }    
}

The code above updates the text fields but the active field with the input freezes after the first key stroke. For example, if you try to enter 60 into the seconds field, the next key stroke does not do anything. The number 6.0 appears after the first key stroke (see image below). This is likely due to the function that is called after the properties are updated. The properties are updated based on the tag of the active text field.

Is it possible to have a function that will update all the other text fields other than the currently active field?


回答1:


Is it possible to have a function that will update all the other text fields other than the currently active field?

Yes. You can connect all text field to the same action.

First connect all your text fields accordingly to your view controller:

@IBOutlet weak var hourField: NSTextField!
@IBOutlet weak var minuteField: NSTextField!
@IBOutlet weak var secondField: NSTextField!

Second create a single var to represent your time interval. You can also add a setter / getter to store it automatically to USerDefaults:

var timeInterval: TimeInterval {
    get {
        return UserDefaults.standard.double(forKey: "timeInterval")
    }
    set {
        UserDefaults.standard.set(newValue, forKey: "timeInterval")
    }
}

Third create a TimeInterval extension to convert the time from seconds to hour/minute

extension TimeInterval {
    var second: Double { return self }
    var minute: Double { return self / 60 }
    var hour:   Double { return self / 3600 }
    var hourToMinute:   Double { return self * 60 }
    var hourToSecond:   Double { return self * 3600 }
    var minuteToHour:   Double { return self / 60 }
    var minuteToSecond: Double { return self * 60 }
}

Fourth create the action that will update the other text fields:

@IBAction func timeAction(sender: NSTextField) {
    // use guard to convert the field string to Double
    guard let time = Double(sender.stringValue) else { return }
    // switch the field  
    switch sender {
    case hourField:
        // convert / update secondary fields
        minuteField.stringValue = time.hourToMinute.description
        secondField.stringValue = time.hourToSecond.description
        // make it persist through launches
        timeInterval = time * 3600
    case minuteField:
        hourField.stringValue = time.minuteToHour.description
        secondField.stringValue = time.minuteToSecond.description
        timeInterval = time * 60
    default:
        hourField.stringValue = time.hour.description
        minuteField.stringValue = time.minute.description
        timeInterval = time
    }
}

Last but not least make sure you load the values next time your view will appear:

override func viewWillAppear() {
    hourField.stringValue = timeInterval.hour.description
    minuteField.stringValue = timeInterval.minute.description
    secondField.stringValue = timeInterval.second.description
}

Sample Project




回答2:


You need to move the setting of the text fields' values out of viewDidLoad() to a separate method. Call that method from viewDidLoad() and also from a didSet property observer on your seconds property.

You also need to create action methods for when the seconds, minutes, or hours fields are changed. In the action method for the seconds field, set the seconds property to secondsField.floatValue. Similarly for the other two fields. In the NIB/storyboard, connects the text fields' actions to the corresponding action method on the view controller.



来源:https://stackoverflow.com/questions/35567895/bind-properties-in-swift-to-nstextfields

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!