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.
lableForValue
, which is last label you have added to View, You need to first take Label according to slider Tag, and then assign String to it. – Eosin