The problem is now fixed. This answer shows an audio application based on the working solution.
Initial question
I am new to Swift and trying to create a bank of UISliders
to test parameters of a physical model in AudioKit. Each UISlider
has two UILabels
, one to identify the name of a parameter, the other to show a current UISlider
value. Tags identify each UISlider
and its corresponding UIlabels.
I am stuck trying to display current UISlider
values in the corresponding UILabel
on an iPhone although I can display these in the debug area in Xcode. When I write slider.value
to its lableForValue
nothing happens except a weird edge condition (see diagram at the bottom).
A log of UISlider
values clearly showed it receiving a value sent and using sender.tag
to identify which UISlider
sent it. But the new value would never appear in the correct UILabel.
Solution
Here is a working solution that will hopefully benefit some other Swift novice. Changes based on the accepted answer have been made to the code below. Tagging lableForValue
with a tag offset before adding it to subview
allowed UILabels
to be more easily identified and rewritten with values read from UISlider.
The accepted answer is also a simple practical demonstration of how to use optionals. A further edge condition has been identified - UILabels
would display values for all sliders except the first - and is corrected here in the final edit. The code also includes an extension of UILabel
used to change the font size.
Thank you PiyushRathi and dijipiji
Final Edit
import UIKit
class ViewController: UIViewController {
var slider: UISlider!
var lableForValue: UILabel!
var lableForID: UILabel!
let defaultColour = UIColor.green
let highlightedColour = UIColor.lightGray
let thumbSize: CGFloat = 20
let topMargin = 75
let verticalSpacing = 50
let sliderWidth = 250
let sliderHeight = 24
let sliderToLabelSpace = 32
let valueLableTagOffset = 1000
let lables = ["intensity",
"dampingFactor",
"energyReturn",
"mainResFreq",
"1stResFreq",
"2ndResFreq",
"amplitude",
"reserved",
"reserved",
"reserved"]
let loLimits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let hiLimits = [100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<10 {
let slider = makeSlider(index: index)
let IDLable = makeIDLable(index: index)
let valueLable = makeValueLable(index: index)
view.addSubview(slider)
view.addSubview(IDLable)
view.addSubview(valueLable)
}
}
func sliderValueChanged(sender: UISlider){
print("SLIDER", sender.tag, ":", sender.value)
var valueLabel: UILabel? = nil
for subview in view.subviews as [UIView] {
if subview.tag > valueLableTagOffset {
print(subview.tag)
let labelTag = subview.tag - valueLableTagOffset
// Edge condition: UILabels display values for all sliders except the first
// Fix: use '- valueLableTagOffset', not '/ valueLableTagOffset'
print(labelTag)
if labelTag == sender.tag {
valueLabel = subview as? UILabel
break
}
}
}
if valueLabel != nil {
valueLabel!.text = String(sender.value)
}
}
func makeHighlightedImage() -> (UIImage) {
let size = thumbSize
let highlightedStateImage = UIImage.createThumbImage(size: size, color: highlightedColour)
return (highlightedStateImage)
}
func makeDefaultImage() -> (UIImage) {
let size = thumbSize
let defaultStateImage = UIImage.createThumbImage(size: size, color: defaultColour)
return (defaultStateImage)
}
func makeValueLable(index: Int) -> UILabel {
let x = Int(view.frame.midX) - (sliderWidth / 2)
let y = Int(topMargin + (verticalSpacing * index) - sliderToLabelSpace)
let w = sliderWidth
let h = sliderHeight
lableForValue = UILabel(frame: CGRect(x: x, y: y, width: w, height: h))
lableForValue.tag = (index + 1) + valueLableTagOffset
// Edge condition: UILabels display values for all sliders except the first
// Fix: use '+ valueLableTagOffset', not '* valueLableTagOffset'
lableForValue.textColor = defaultColour
lableForValue.textAlignment = NSTextAlignment.center
lableForValue.text = String(index + 1)
return lableForValue
}
func makeIDLable(index: Int) -> UILabel {
let x = Int(view.frame.midX) - (sliderWidth / 2)
let y = Int(topMargin + (verticalSpacing * index) - 32)
let w = sliderWidth
let h = sliderHeight
lableForID = UILabel(frame: CGRect(x: x, y: y, width: w, height: h))
lableForID.tag = index + 1
lableForID.textColor = highlightedColour
lableForID.textAlignment = NSTextAlignment.left
lableForID.defaultFont = UIFont(name: "HelveticaNeue", size: CGFloat(12))
lableForID.text = lables[index]
return lableForID
}
func makeSlider(index: Int) -> UISlider {
let x = view.frame.midX
let y = CGFloat(topMargin + (verticalSpacing * index))
let w = sliderWidth
let h = sliderHeight
slider = UISlider(frame: CGRect(x: 0, y: 0, width: w, height: h))
slider.center = CGPoint(x: x, y: y)
slider.minimumValue = Float(loLimits[index])
slider.minimumTrackTintColor = defaultColour
slider.maximumValue = Float(hiLimits[index])
slider.maximumTrackTintColor = highlightedColour
slider.tag = index + 1
slider.value = slider.maximumValue / 2.0
slider.isContinuous = false
slider.addTarget(self, action: #selector(sliderValueChanged), for: UIControlEvents.valueChanged)
return slider
}
}
UILabel+FontFiddler
This extension is necessary to get a different sized font for labelForID
and labelForValue
import UIKit
extension UILabel{
var defaultFont: UIFont? {
get { return self.font }
set { self.font = newValue }
}
}
Edge Condition
The screen-shot below shows what happens to the last UILabel
when any slider is moved. The value displayed is always 50.0 no matter which slider is moved or how far. I do know the condition disappears when I disable the statement that reads a value from slider 10. But I can't tell how a value of 50 always appears in the UILabel for slider 10 whenever other sliders are moved.
It looks like you have these member variables: var lableForValue: UILabel! var lableForID: UILabel!
In your func makeValueLable you should consider creating a fresh label instead of referencing the member variables. In short - ditch you member variables for these UILabels and inside makeValueLable and makeIDLabel create fresh instances: let labelForValue = UILabel() let lablelFotID = UILabel() etc.
Note - you spell "label" as "lable" quite a bit :)
Hi you have to do several changes in your code:
You have to pass a
unique tag
value to eachlableForValue
so it can be found easily in UIView.e.g. for adding label in
func makeValueLable(index: Int) -> UILabel
function putlableForValue.tag = (index + 1) * 1000
Change
func sliderValueChanged(sender: UISlider){
to this:hope this helps.
Here is how
UISliders
may be used to change parameters in physical modelling audio synthesis. You will needAudioKit.framework.
Instructions for downloading and using it can be found here.The model is essentially a chaotic system for synthesising sounds made by dripping water. Some sliders have more effect than others but it is
dampingFactor
that excites the physical model. At first an isolated drip sound can be heard when this is changed, but, like standard plumbing fixtures, if you fiddle with it long enough you will have a steady flow of dripping sounds that may be difficult (but not impossible) to stop. Three sliders for resonant frequency affect the pitch of the sound.Extension 1 UILabel+FontFiddler
Extension 2 UIImage+DrawCircle
Thank you McMatan