I need to develop custom UISlider using swift. The problem is I don't know how to make sure label following slider's thumbnail like following below:
My code so far
import Foundation
import UIKit
class Slider: UISlider {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 0.2
self.tintColor = UIColor.redColor()
}
}
Probably is worth to take a look at this method, this can't be called directly but you can use it while subclassing
func thumbRectForBounds(_ bounds: CGRect,
trackRect rect: CGRect,
value value: Float) -> CGRect
More info also on this question and here
I had the exact thing for audio files.
The protocol is used to inform the player when user changes the value of the slider.
func updateSliderPosition(position: Float)
is called every half second by the player to change position of the slider current value.
import UIKit
protocol AudioSliderControlDelegate: class
{
func audioSliderControlDdiChangeValue(sender: AudioSliderControl, value: Float)
}
class AudioSliderControl: UIControl
{
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var currentPositionLabel: UILabel!
#IBOutlet weak var totalDurationLabel: UILabel!
weak var delegate: AudioSliderControlDelegate?
private var totalLenght: Double?
override func awakeFromNib()
{
super.awakeFromNib()
backgroundColor = UIColor.clevooOrange()
slider.addTarget(self, action: "sliderValueChanged:", forControlEvents: .ValueChanged)
self.addSeperatorToTop()
}
//MARK:
//MARK: - Setup
func setupForDuration(duration: Double)
{
totalLenght = duration
currentPositionLabel.text = Double(0).formattedDuration()
totalDurationLabel.text = (duration + 0.5).formattedDuration()
slider.setValue(0, animated: false)
}
func updateSliderPosition(position: Float)
{
self.currentPositionLabel.text = (totalLenght! * Double(position)).formattedDuration()
self.slider.value = position
}
//MARK:
//MARK: - Notification
func sliderValueChanged(slider: UISlider)
{
self.currentPositionLabel.text = (totalLenght! * Double(slider.value)).formattedDuration()
delegate?.audioSliderControlDdiChangeValue(self, value: slider.value)
}
}
Related
I am a beginner so bear with me.
I created a basic timer app and rounded the corners of two buttons. The button was still clickable outside of the circle, I needed to fix that so I found a solution online that said to insert the code into the UIButton's subclass or extension. I found solutions for both ways and chose extension option because the code was easier to read and I could understand it better.
The issue now is that I have a third UIButton that is being affected by the extension and I would like to exclude it. I'm not sure if this is even practical (or possible) so please correct me if there is a better way to approach this. The button I need to exclude from the extension is resetButton.
import UIKit
extension UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
//exclude resetButton???
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var stopButton: UIButton!
#IBOutlet weak var resetButton: UIButton!
var timeRemaining: Int = 10
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//// Moved to an extension in order to
//// remove the clickable areas outside
//// of the circle
//startButton.layer.cornerRadius = 50.0
//stopButton.layer.cornerRadius = 50.0
}
#IBAction func start(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: Any) {
timer?.invalidate()
}
#IBAction func reset(_ sender: Any) {
timer?.invalidate()
timeRemaining = 10
label.text = "\(timeRemaining)"
}
#objc func step() {
if timeRemaining > 0 {
timeRemaining -= 1
} else {
timer?.invalidate()
}
label.text = "\(timeRemaining)"
}
}
For your case, extension is not the right option as the methods invoked from extension will apply to the type itself (all UIButton objects in this case).
One option for you is to make a subclass of UIButton instead of extension. Something like this:
class RoundedButton: UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
And hence you can select the buttons that should inherit the custom layout from the class above:
#IBOutlet weak var startButton: RoundedButton!
#IBOutlet weak var stopButton: RoundedButton!
#IBOutlet weak var resetButton: UIButton! // Will not get the style applied for startButton and stopButton
I was creating animations for buttons using extension in swift but when i call the animation function it generates error.
import UIKit
extension UIButton{
func wiggle() {
let wiggleAnim = CABasicAnimation(keyPath: "psoition")
wiggleAnim.duration = 0.05
wiggleAnim.repeatCount = 5
wiggleAnim.autoreverses = true
wiggleAnim.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
wiggleAnim.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
layer.add(wiggleAnim, forKey: "position")
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var colorizeBtn: UIButton!
#IBOutlet weak var wiggleBtn: UIButton!
#IBOutlet weak var dimBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func colorizeBtnWasPressed(_ sender: Any) {
}
#IBAction func wiggleBtnWasPressed(_ sender: Any) {
wiggleBtn.wiggle()
}
#IBAction func dimBtnwasPressed(_ sender: Any) {
}
}
View Controller
Your wiggleBtn is of type UIView but you write an extension to UIButton.
Either change the extension to UIView or change the type of wiggleBtn to UIButton.
I need help to accomplish this:
Its a group of 3 photos. The first photo is the button screen before the click, the second photo is the screen after the click and the third photo is the way i've designed in the IB using stackview
I've being trying to create this and this is the result i've got so far. I still haven't created anything as the After button click image shows because of this:
My Result now
As you can see in the gif when i press the button, the UIView height constraint.constant is set to a higher value and the UIView get higher. All i want is the stripped background to get higher as well.
This is the way the view is disposed in IB.
And finally, this is the way i've coded
class LetterScreenViewController: UIViewController, ResizeWordViewDelegate {
#IBOutlet weak var youTubeView: YouTubeView!
#IBOutlet weak var phonemeView: PhonemeView!
#IBOutlet weak var wordView: WordView!
var letterPresenter: LetterPresenter?
#IBOutlet weak var heightWordViewConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
delegateWordObj()
if let presenter = letterPresenter {
presenter.setupView()
}
}
#IBAction func dismissLetter(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func delegateWordObj() {
wordView.resizeWordViewDelegate = self
}
func didPressButton() {
if heightWordViewConstraint.constant <= 0 {
heightWordViewConstraint.constant = 30
}else{
heightWordViewConstraint.constant = 0
}
}
}
import UIKit
protocol ResizeWordViewDelegate {
func didPressButton()
}
class WordView: UIView {
#IBOutlet weak var backgroundListras: UIImageView!
#IBOutlet var WordView: UIView!
#IBOutlet weak var showWordButton: UIButton!
var resizeWordViewDelegate: ResizeWordViewDelegate!
override init(frame: CGRect) {
super.init(frame: frame)
xibInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibInit()
}
func xibInit() {
Bundle.main.loadNibNamed("WordView", owner: self, options: nil)
addSubview(WordView)
WordView.frame = self.bounds
WordView.round(corners: [.topLeft,.topRight], radius: 120)
WordView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
#IBAction func showWordButtonAction(_ sender: Any) {
resizeWordViewDelegate.didPressButton()
self.frame.size = CGSize(width: 375, height: 200)
self.backgroundListras.frame.size = CGSize(width: 375, height: 200)
}
}
I want to know a way to resize this UIView with all the content in it. After finding a solution to increase the height i'll be able to put all the others components shown when i press the button.
I believe that your goal is to create something like this:
All I had to do was change the height of the constraint programmatically
Check the solution in the project below:
https://gitlab.com/DanielLimaDF/ResizeTest.git
Important: Beware of addSubview, it can remove constraints from a View if the constraints were created before calling addSubview
I am working on iOS application in swift 3.0 where I am creating custom view with textfield and button calculate values for all text filed and display the sum of all textfield on the top totalText.
Code for MainViewController:
#IBOutlet weak var totalText: UITextField!
var totalview:[UIView]!
override func viewDidLoad() {
yvalue = 1
tag = 1
count = 1
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func actionButton(_ sender: UIButton) {
yvalue = 55 + yvalue
//for i in 0...count {
extraview = View(frame: CGRect(x: 50, y: 75+yvalue, width: 350, height: 50))
extraview.backgroundColor = UIColor(white: 1, alpha: 0.5)
extraview.layer.cornerRadius = 15
extraview.tag = tag
print("ExtraView tag=",extraview.tag)
extraview.ActionButtonsub.addTarget(self, action: (#selector(cancelbutton(_:))), for: UIControlEvents.touchUpInside)
extraview.textFiled.addTarget(self, action: #selector(didChangeTexts(textField:)), for: .editingChanged)
extraview.textFiled.tag = tag
print("text tag=",extraview.textFiled.tag)
self.view.addSubview(extraview)
count = count + 1
tag = tag + 1
//}
}
func cancelbutton(_ sender: UIButton) {
extraview.removeFromSuperview()
}
func didChangeTexts(textField: UITextField) {
totalText.text = extraview.textFiled.text
}
Code for UIView:
class View: UIView {
#IBOutlet var subView: UIView!
#IBOutlet weak var textFiled: UITextField!
#IBOutlet weak var ActionButtonsub: UIButton!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Bundle.main.loadNibNamed("View",owner: self, options:nil)
self.addSubview(self.subView)
}
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("View", owner: self, options: nil)
subView.frame = bounds
self.addSubview(self.subView)
}
}
Sample Output
If you think #Scriptable way is complex then go like below..
Simple way, you can add those textfields into one collection while creating & looping it when you need to do something.
Something like this should work, may need a little modification.
You just need to iterate through the subviews, get the textfield for each view, convert to double value and add them up along the way.
func calculateTotal() -> Double {
var total: Double = 0.0
for subview in self.view.subviews {
if let v = subview as? View, !v.textFiled.isEmpty {
total += Double(v.textFiled.text)
}
}
return total
}
An alternative is applying filter, flatMap and reduce functions
let sum = (view.subviews.filter{$0 is View} as! [View]) // filters all `View` instances
.flatMap{$0.textFiled.text} // maps all text properties != nil
.flatMap{Double($0)} // maps all values which are convertible to Double
.reduce(0.0, {$0 + $1}) // calculates the sum
I'm trying to initialize a custom UIButton class, but I'm doing it wrong and not sure how to implement this. I built a custom UIButton class and initialized it in an IBOutlet. The button works fine but none of the properties that I set are displayed.
import UIKit
import Foundation
class WBCircleButton : UIButton {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderColor = UIColor.blueColor().CGColor
self.layer.borderWidth = 2
self.layer.cornerRadius = self.frame.size.width / 2
}
}
class WBMainViewController: UIViewController {
var timeControl = WBTimeController()
var timer = NSTimer()
#IBOutlet weak var timeDisplay: UILabel!
//this doesn't work as expected ?????
#IBOutlet weak var startButton: WBCircleButton!
//********************************************
#IBAction func startTimer(sender: AnyObject) {
timeControl.startTimer()
startButton.setTitle("Stop", forState: UIControlState.Normal)
let aSelector: Selector = "updateTimerDisplay"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
}
//********************************************
func updateTimerDisplay () {
timeDisplay.text = timeControl.timeLabelText
}
//********************************************
override func viewDidLoad() {
super.viewDidLoad()
}
//********************************************
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I think you didn't validate something. I created a Xcode Single View Application template project and did the following (check each step to see what is missing for you).
In your project navigator:
Create a new UIViewController class file, name it "WBMainViewController" and set this code in it:
import UIKit
class WBMainViewController: UIViewController {
#IBOutlet weak var startButton: WBCircleButton!
override func viewDidLoad() {
super.viewDidLoad()
startButton.setTitle("Stop", forState: UIControlState.Normal)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Create a new UIButton class file in your project, name it "WBCircleButton" and set the following code in it:
import UIKit
class WBCircleButton: UIButton {
required init(coder decoder: NSCoder) {
super.init(coder: decoder)
layer.borderColor = UIColor.blueColor().CGColor
layer.borderWidth = 2
layer.cornerRadius = self.frame.size.width / 2
}
}
In Interface Builder:
Select your UIViewController scene and set its class to "WBMainViewController" in the Identity Inspector.
Add an UIButton to your scene, set auto layout constraints to it and set your UIButton class to "WBCircleButton" in the Identity Inspector.
Select your ViewController scene and click on the "Show the assistant Editor". Be sure to display your ViewController code and drag your startButton IBOutlet on the UIButton in your Interface Builder scene.
Launch your project.
I was able to display this big button in my ViewController after launching my project in the simulator: