Updating NSTextField when NSPopupButton selection changes

***Edit: The more I look at this, the more I’m convinced that the issue is that the outputText NSTextField is not coded to update when the codecSelector NSPopupButton changes (or the codecSelector popup is not coded to force a change to outputText). So I suppose I need to change my question:

How do I compel a change in the outputText NSTextField so that it gets a new value from codecChosen variable when codecChosen is changed by a selection in the codecSelector NSPopupButton method?

My other question, regarding how to establish a default codec choice, remains, though of course that default choice also needs to register in the outputText NSTextField.

I’m trying to create a ffmpeg GUI that will allow users to convert their audiobooks to other formats

I have two text fields in my project. One of them is populated by the stringValue of the input file chosen. That part was relatively easy.

27%20PM

The output text field is a little tougher. If the user doesn’t select an alternate location or codec for their audiobook file, it defaults to the location and filename of the input book, and the default codec settings.

However, if the user chooses a different output directory, the field needs to:

  • get the directory data from the output Browse button
  • derive the filename from the input file (the user can edit that if they wish, but the default is the same filename as the input file)
  • get the extension from the codec NSPopupBox.

46%20PM

It’s that last part I’m having issues with now.

In my variables, I declared this:

var codecChosen: String = ""

Then I set up my @IBAction like so:

@IBAction func codecSelector(_ sender: Any) {
	let codecChoice = codecSelector.titleOfSelectedItem
	if codecChoice == ".MP3 (Lossy)" {
			codecChosen = ".mp3"
			ffmpegCodecArg = "-c:a libmp3lame"
			samplingSelector.isEnabled = true
			bitrateSelector.isEnabled = true
			vbrSwitch.isEnabled = true
			qualitySelector.isEnabled = true
			stereoSwitch.isEnabled = true
	} else if codecChoice == "Flac" {
			codecChosen = ".flac"
			ffmpegCodecArg = "-c:a flac"
			samplingSelector.isEnabled = false
			bitrateSelector.isEnabled = false
			vbrSwitch.isEnabled = false
			qualitySelector.isEnabled = false
			stereoSwitch.isEnabled = false
	} else {
			codecChosen = ".m4b"
			ffmpegCodecArg = "-c copy"
			samplingSelector.isEnabled = false
			bitrateSelector.isEnabled = false
			vbrSwitch.isEnabled = false
			qualitySelector.isEnabled = false
			stereoSwitch.isEnabled = false
	}
	ffmpegArguments.updateValue("\(ffmpegCodecArg)", forKey: "codecKey")
	print(ffmpegArguments)
}

And my @IBAction for the outputBrowse button is set up to (one would hope) derive the file extension from the codecChosen variable, as follows:

// user has option to choose another output directory
@IBAction func outputBrowseClicked(_ sender: Any) {
	let outputPanel = NSOpenPanel()
	outputPanel.canChooseFiles = false
	outputPanel.canChooseDirectories = true
	outputPanel.allowsMultipleSelection = false
	let userChoice = outputPanel.runModal()
	switch userChoice{
		case .OK :
			if let outputDirectory = outputPanel.url {
				if defaultUrl != nil {
				let outputFile = defaultUrl.deletingPathExtension()
				let outputFilename = outputFile.lastPathComponent
				outputPath = outputDirectory.appendingPathComponent(outputFilename)
				outputText.stringValue = outputPath.path + "\(codecChosen)"
				ffmpegArguments.updateValue("outputText.stringValue", forKey: "outputTextKey")
		}
		case .cancel :
			print("user cancelled")
		default:
			break
	}
}

But for some reason, it’s not happening.

If I choose the codec from the dropdown FIRST, the outputText field (which is populated by outputText.stringValue = outputPath.path + "\(codecChosen)") will derive the correct extension, but it won’t change if I change my selection using the codec NSPopupButton.

.40%20PM

If I don’t use the NSPopup selector first, the outputText field never gets the extension from the codecChosen variable.

I’ve tried moving the methods related to populating the outputText NSTextField to a separate function instead of leaving them in the outputBrowse @IBAction, because right now it feels like I’m violating the DRY for several lines of code in the @inputBrowse and @outputBrowse actions, but I haven’t gotten that to work yet. But that’s outside the scope of this particular question.

My other issue specifically pertaining to the NSPopupButton is that it doesn’t actually seem to have a default selection. The it appears to, but even though it DISPLAYS the first item as a default, it doesn’t actually run the portion of the method as though that selection has been made. That should happen by default.

I’m not sure how to fix either of those issues. I’ve tried rearranging the order of the code in various ways, but that didn’t seem to make a difference in anything except the order in which my ffmpegArguments dictionary was printed when I checked it.

The NSPopup action is working perfectly in regards to adding the appropriate key/value pairs to the ffmpegArguments dictionary based on what is selected in the popup, but only if I actually select something. Again, it doesn’t register the first item by default.

I suspect this is all related to the order in which things are happening, but I just can’t figure out the proper way to structure it. Specifically, I need to know what command to use in order to get the NSTextField for the output file to update when a new codec is chosen in the NSPopupButton.

After researching all day, I finally solved this one.

Instead of putting the code to populate the NSTextField in the @IBAction for the Browse button, I created a separate method for it that I call in the action for the browse button. Same thing with the NSPopupButton. Instead of putting the switch statements in the @IBAction, I made a separate method to do it.

Then I call the methods when the actions are performed for the Browse buttons or the popup button, so the text fields get updated whenever any of those actions are performed.

	// user has option to choose another output directory
@IBAction func outputBrowseClicked(_ sender: Any) {
	let outputPanel = NSOpenPanel()
	outputPanel.canChooseFiles = false
	outputPanel.canChooseDirectories = true
	outputPanel.allowsMultipleSelection = false
	let userChoice = outputPanel.runModal()
	switch userChoice{
		case .OK :
			if let outputUrl = outputPanel.url {
				outputDirectoryUrl = outputUrl
				outputDirectoryPath = outputUrl.path
				updateOutputText(outputString: outputFilePath, outputField: outputText)
		}
		case .cancel :
			print("user cancelled")
		default:
			break
	}
}

// user chooses output codec - default is AAC/M4B
@IBAction func codecSelector(_ sender: Any) {
	codecSelection() // call codec selection method
	updateOutputText(outputString: outputFilePath, outputField: outputText) // update outputText field with appropriate file extension
}


// MARK: Methods
// update input and output text fields
func updateInputText(inputString: String, inputField: NSTextField) {
	codecSelection()
	inputText.stringValue = inputFilePath
	}

func updateOutputText(outputString: String, outputField: NSTextField) {
	codecSelection()
	if inputFileUrl != nil && outputDirectoryUrl == nil {
		let outputFileUrl = inputFileUrl!.deletingPathExtension()
		outputFilePath = outputFileUrl.path + "\(codecChosen)"
	} else if	inputFileUrl != nil && outputDirectoryUrl != nil {
		let outputFile = inputFileUrl!.deletingPathExtension()
		let outputFilename = outputFile.lastPathComponent
		let outputDirectory = outputDirectoryPath
		outputFilePath = outputDirectory + "/" + outputFilename + "\(codecChosen)"
	}
	outputText.stringValue = outputFilePath
}

@ncrusher Thank you for sharing your solution - much appreciated! :]

This topic was automatically closed after 166 days. New replies are no longer allowed.