programmatically have slider set label with in a uitableview cell - ios

Im trying to update my label when the slider moves though I may be missing something? perhaps the slider is not connected to the function?
let slider: UISlider = {
let s = UISlider()
s.minimumValue = 0
s.maximumValue = 1000
s.isContinuous = true
s.tintColor = UIColor.blue
s.value = 500
s.addTarget(self, action: #selector(paybackSliderValueDidChange),for: .valueChanged)
return s
}()
func paybackSliderValueDidChange(sender: UISlider!)
{
print("payback value: \(sender.value)")
sliderLabel.text = "\(sender.value)"
}

Make sure to mark your paybackSliderValueDidChange(sender:) method as #objc. Otherwise, UIKit won't be able to access it in Swift 4

just had to change "let slider: UISlider" to "lazy var slider: UISlider"
and voila

Related

Is there a way to programmatically associate a variable and a target object with the control in swift?

I'm creating a slider programmatically due to and Xcode bug, (don't let me center the thumb when I change the slider values, so I decided to do it using code) and I want to have a variable which saves the slider value. Is there a way to associate the variable and the target object with the control, similar to "addTarget", but instead of an action, its a variable?
I don't know if I explained myself but tell me if I need to be more specific. Thanks in advance :) for helping me.
This is my code:
import UIKit
class ViewController: UIViewController {
var slider: UISlider!
#IBOutlet weak var sliderVar: UISlider!
var currentSliderValue = 0
override func viewDidLoad() {
super.viewDidLoad()
slider = UISlider(frame: CGRect(x: 98, y: 173, width: 699, height: 30))
slider.center = self.view.center
slider.minimumValue = 1
slider.maximumValue = 100
slider.value = 50
slider.isContinuous = true
slider.addTarget(self, action: #selector(sliderMoved(_:)), for: UIControl.Event.valueChanged)
self.view.addSubview(slider)
}
#IBAction func sliderMoved(_ sender: UISlider) {
currentSliderValue = lroundf(sender.value)
}
}
My function “sliderMoved” changes the sliderCurrentValue variable, but this var won’t change until I use the slider and move it. I also have a button there, that when you touch it up it shows the slider value, but the “sliderCurrentValue” only changes its value when the slider is moved. I was thinking of creating an IBOutlet but I don’t know how to connect this one with the slider.
As far as I understand you need to bind variable to slider's value. There are several ways to achieve it. There is one way. Declare variable with custom setters and getters
class ViewController: UIViewController {
var slider: UISlider!
var sliderValue: Float {
set {
// use optional chaining here helps avoid crashes
slider?.setValue(newValue, animated: true)
}
get {
// if slider control hasn't created yet you need to return some dummy value
slider?.value ?? -1
}
}
func viewDidLoad() {
// your current implementation here
}
}

How to get switch on/off status for switches created and added in a cycle to the stackView in Swift

I am creating UIStackView and in a cycle creating some amount of UILabel and UISwitch pairs that are added to UIStackView. How now I can get the status of UISwitch is it ON or OFF?
func addAnswrsToTheStack(using answers: [Answer]){
createHorizontalStackView()
horizontalStackView.addArrangedSubview(questionProgressView)
var questionSwitch: UISwitch!
for answer in answers {
createHorizontalStackView()
let label: UILabel = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.textAlignment = .left
label.text = answer.text
horizontalStackView.addArrangedSubview(label)
questionSwitch = UISwitch()
questionSwitch.isOn = false
horizontalStackView.addArrangedSubview(questionSwitch)
}
}
You can try
for (index,answer) in answers.enumerated() {
....
questionSwitch = UISwitch()
questionSwitch.tag = index
questionSwitch.isOn = false
questionSwitch.addTarget(self, action: #selector(switchChanged), for:.valueChanged)
}
#objc func switchChanged(_ mySwitch: UISwitch) {
let value = mySwitch.isOn
print(mySwitch.tag , value)
}
You get the value of a UISwitch the same way you set it, by calling isOn. You can loop through all the views in your stackView, check if the view is a switch with
for view in horizontalStackView.arrangedSubviews {
if view is UISwitch {
// check the value here
}
}
You don't know the state state of a switch for each answer since you only create the switch, but you don't save that association. A clean way would be a wrappertype that has a Answer and a UISwitch. But let's do it without for now.
You need return a map that tells you what switch is related what question. The type [Answer:UISwitch] will work and thats it. You now can look up each Switch and its state for an Answer.
func addAnswrsToTheStack(using answers: [Answer]) -> [Answer:UISwitch] {
createHorizontalStackView()
horizontalStackView.addArrangedSubview(questionProgressView)
var questionSwitch: UISwitch!
var switchMap = [Answer:UISwitch]()
for answer in answers {
createHorizontalStackView()
let label: UILabel = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.textAlignment = .left
label.text = answer.text
horizontalStackView.addArrangedSubview(label)
questionSwitch = UISwitch()
questionSwitch.isOn = false
switchMap[Answer] = questionSwitch
horizontalStackView.addArrangedSubview(questionSwitch)
}
return switchMap
}

How to go back when the UITextfield is empty in Swift code?

My question is: When the UITextField is empty, how do I click the "Backspace" button to go to the previous UITextField? I have been struggling trying to do this in my code below?
Second Question: How do I only allow 1 character to get entered in the UITextField?
I am new at Swift code and trying to learn. Any help would be great.
What I am trying to do is have the user be able to type in a code in the 6 UITextFields and be able to click the "Backspace" button on any one of the UITextFields with only allowing the user to enter one number in each UITextField.
Code Below:
#objc func textFieldDidChange(textfield: UITextField) {
let text = textfield.text!
if text.utf16.count == 0 {
switch textfield {
case textField2:
textField1.becomeFirstResponder()
textField1.backgroundColor = UIColor.blue
textField1.tintColor = .clear
case textField3:
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.blue
textField2.tintColor = .clear
case textField4:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.blue
textField3.tintColor = .clear
case textField5:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.blue
textField4.tintColor = .clear
case textField6:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.blue
textField5.tintColor = .clear
textField6.resignFirstResponder()
textField6.backgroundColor = UIColor.blue
textField6.tintColor = .clear
default:
break
}
}
else if text.utf16.count == 1 {
switch textfield {
case textField1:
textField1.backgroundColor = UIColor.black
textField1.textColor = .white
textField1.tintColor = .clear
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.black
textField2.textColor = .white
textField2.tintColor = .clear
case textField2:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.black
textField3.textColor = .white
textField3.tintColor = .clear
case textField3:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.black
textField4.textColor = .white
textField4.tintColor = .clear
case textField4:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.black
textField5.textColor = .white
textField5.tintColor = .clear
case textField5:
textField6.becomeFirstResponder()
textField6.backgroundColor = UIColor.black
textField6.textColor = .white
textField6.tintColor = .clear
case textField6:
textField6.resignFirstResponder()
default:
break
}
}
}
I'd just like to point out that I'm still relatively new to iOS and Swift in general, but even with just a few minutes of searching, I was able to find some seeds of ideas which provided me with the suggested solution.
Based on your (improved) question, I believe a different approach is required. What you really don't want to use a text component. "Why"?
I here you ask. Because they don't actually provide you with the functionality that you want and come with a considerable overhead.
For this, what you really want is more control. You want to know when a key is pressed and you want to respond to it (I know, sounds like a text component, but) and be notified when more extended functionality occurs, like the delete key is pressed.
After a few minutes of research, some trial and error, I found that the UIKeyInput is more along the lines of what you want.
It will tell you when text is inserted and, more importantly, will tell you when Delete is pressed
The added benefit is, you can filter the input directly. You can take the first character from the String and ignore the rest or auto fill the following elements with the remaining text. You can perform validation (for numerical only content) and what ever else you might want to do
So, I started a really new project, added a UILabel to the UIViewController in the storyboard, bound it to the source and implemented the UIKeyInput protocol as such...
class ViewController: UIViewController {
override var canBecomeFirstResponder: Bool {
return true
}
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
becomeFirstResponder()
}
}
extension ViewController: UIKeyInput {
var hasText: Bool {
return true
}
func insertText(_ text: String) {
print(text)
label.text = text
}
func deleteBackward() {
print("Delete backward")
}
}
I ran the project and when a key was typed, the label was updated with the new key and when delete was pressed, the Delete backward text was printed to console.
Now. You have some choices to make. To use a single UIViewController and (maybe) a series of UILabels and manage interactions within it, so when a key is typed, you present the next label as the input focus (and when delete is pressed, you move back) or do you create a series of UIControls which represent each digit and manage via some delegate call back process.
You may also need to implement the UITextInputTraits protocol, which will allow you to control the keyboard presented
You might also like to have a read through Responding to Keyboard Events on iOS, CustomTextInputView.swift and Showing the iOS keyboard without a text input which were just some of the resources I used to hobble this basic example together with.
you can use this extension for your second question:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self] else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
#objc func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
when you add this extension to your project you can see an extra attribute in "Attribute Inspector" tab and you can set the max length of UITextField.

Detect UIPageControl page change

I want to Add a UIPageController inside UICollectionViewCell. I created a custom nib file with a collectionViewCell inside. Inside the cell I added a UIPageControl.
#IBOutlet weak var pageControl: UIPageControl!
override func awakeFromNib() {
super.awakeFromNib()
self.isUserInteractionEnabled = true
pageControl.isUserInteractionEnabled = true
self.backgroundColor = UIColor.darkGray
setupPageControl()
}
private func setupPageControl() {
pageControl.numberOfPages = 7
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = UIColor.orange
pageControl.pageIndicatorTintColor = UIColor.lightGray.withAlphaComponent(0.8)
}
How can I detect the page change? Also the dots are in the middle of the view, is it possible to put them on the middle?
Swift 5
1) addTaget
You can add a target that detect value change on your setupPageControl functions:
private func setupPageControl() {
pageControl.numberOfPages = 7
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = UIColor.orange
pageControl.pageIndicatorTintColor = UIColor.lightGray.withAlphaComponent(0.8)
pageControl.addTarget(self, action: #selector(pageControlHandle), for: .valueChanged)
}
Create a selector to change the value that you wants:
#objc private func pageControlHandle(sender: UIPageControl){
print(sender.currentPage)
}
From Apple Documentation
When a user taps a page control to move to the next or previous page,
the control sends the valueChanged event for handling by the
delegate. The delegate can then evaluate the currentPage property to
determine the page to display. The page control advances only one page
in either direction. The currently viewed page is indicated by a white
dot. Depending on the device, a certain number of dots are displayed
on the screen before they are clipped.
Also you can use
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}

Strange bug on dequeuing collectionviewcell

I have a calendarView made up of collectionView. It is a custom calendarView derived using mathematical calculations.
The seventh row marks Saturday and it's holiday so the font color is red for the all the labels of seventh column.
However, when I swipe or navigate to other days, the red color labels are scattered in random order which is untraceable. A screenshot is herewith:
How did this occur?
In my dequeueReusableCell method I have cell configured for holiday as:
cell.isHoliday = (indexPath.row + 1) % 7 == 0 ? true : false
And this is the logic for holiday in my custom collectionViewCell.
#IBOutlet var dateLabel: UILabel!
#IBOutlet var englishDateLabel: UILabel!
#IBOutlet var tithiLabel: UILabel!
var isToday: Bool = false {
didSet {
self.contentView.backgroundColor = isToday ? Colors.Palette.LightGreen : UIColor.white
}
}
var isHoliday: Bool = false {
didSet {
if isHoliday {
tithiLabel.textColor = Colors.Palette.DarkRed
dateLabel.textColor = Colors.Palette.DarkRed
englishDateLabel.textColor = Colors.Palette.DarkRed
}
else {
dateLabel.textColor = UIColor.black
englishDateLabel.textColor = UIColor.black
}
}
}
The number of red labels on top of each collectionview cells goes on increasing as I swipe to next month. Why is this happening and how can I stop this from happening?
You are missing else part:
var isHoliday: Bool = false {
didSet {
if isHoliday {
tithiLabel.textColor = Colors.Palette.DarkRed
dateLabel.textColor = Colors.Palette.DarkRed
englishDateLabel.textColor = Colors.Palette.DarkRed
}
else {
tithiLabel.textColor = UIColor.black
dateLabel.textColor = UIColor.black
englishDateLabel.textColor = UIColor.black
}
}
}
This may be because the cell is being reused and you are not implemented any logic in prepareForReuse method of your custom cell class. In this method try setting text colour properties to nil.
The right way to deal with old data showing up in reused cells is to override prepeareForReuse in your custom cell
open override func prepareForReuse() {
super.prepareForReuse()
tithiLabel.textColor = UIColor.black
dateLabel.textColor = UIColor.black
englishDateLabel.textColor = UIColor.black
}
Clear out old values (by assigning them to nil) or set defaults to all values that might not necessarily be set after the cell is reused. This way, even if the new value(s) are not explicitly set to the cell, you are sure that old values are not being retained.

Resources