Updating progress bar after user selects an item from array - swift

I am trying to update the progress bar after the user selects the desired value.
Values are taken from model and assigned to the label:
let volumeArray = [20, 50, 100, 150, 200, 250, 300, 330, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1500, 2000]
Custom progress bar code:
let center = view.center
let trackLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 1.5 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = .round
view.layer.addSublayer(trackLayer)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.systemBlue.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = .round
shapeLayer.strokeEnd = 0
view.layer.addSublayer(shapeLayer)
But how can I make sure that the progress bar is updated when the user selects a value. For example, from an array let volumeArray = [20, 50, 100, 150, 200, 250, 300, 330, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1500, 2000]
But how can I make sure that the progress bar is updated when the user selects a value. For example, the user selected the number 100 from the array. The maximum value that the user must type, for example, is 2000. And based on the maximum value and the value selected by the user, the progress bar is updated by the corresponding percentage. I wrote a function
func updateProgress() {
if progressBar.progress != 1 {
progressBar.progress += Float(milileters2.volumeFromMilimetersVC / 10)
}
shapeLayer.strokeEnd = progress
}
But unfortunately, it does not work. I would be grateful for any help since I am still a beginner
UPD. Data is taken from another ViewController
var addDrinks = AddDrinksViewController()
var drinksToMainVC: [String] = []
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? MainViewController
vc?.delegate = self
vc?.addVolume(volumeFromMilimetersVC)
vc?.addedDrinksArray.append(contentsOf: drinksToMainVC)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
volumeFromMilimetersVC = volume.volumeArray[row]
drinksToMainVC.append(drinks?.imageName ?? "nil")
}
Attached gif. As you can see, a value is selected from Picker View, which is then successfully written to the label, but the round progress bar is not updated.

In the function updateProgress, it is not shown where progress variable is getting updated. Progress variable can be updated as below.
let volumeArray = [20, 50, 100, 150, 200, 250, 300, 330, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1500, 2000]
let volumeMax = volumeArray.max()
//strokeEnd needs to be between 0 and 1, 2000 will correspond to 1 and others as a fraction of 1
let progress = CGFloat(milileters2.volumeFromMilimetersVC)/CGFloat(volumeMax!)
shapeLayer.strokeEnd = progress

Related

Looping animations with duration, Swift 4

I have an image that I want to move 10 separate times in a row. Each time I want it to do a check, as notated by the print statement of checkForStuff. I noticed that the loop starts and it times out while the first of the 10 moves occurs. Any thoughts on how to chain animations, or do I need to manually write each chain in the sequential closure?
#objc func moveBox(sender: UIButton!) {
print("Started")
let imageToMove = UIImageView()
imageToMove.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
imageToMove.backgroundColor = .red
self.view.addSubview(imageToMove)
var counter = 0
var altitude = 100
while counter <= 10 {
print("looping")
UIView.animate(withDuration: 0.5, animations: {
imageToMove.frame = CGRect(x: 50, y: altitude, width: 50, height: 50)
}, completion: { finished in
counter += 1
altitude += 50
print("Check For Stuff")
})
}
}
In short, in should move 50 pixels down, execute the closure, move 50 pixels down again. and again, and again.
Try Recursion as suggested by #matt
func animate(view: UIView, altitude: Int, numberOfTime: Int ) {
if numberOfTime == 0 { return }
UIView.animate(withDuration: 0.5, animations: {
view.frame = CGRect(x: 50, y: altitude, width: 50, height: 50)
}, completion: { finished in
self.animate(view: view, altitude: altitude + 50, numberOfTime: numberOfTime - 1)
})
}
Use this as
#objc func moveBox(sender: UIButton!) {
print("Started")
let imageToMove = UIImageView()
imageToMove.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
imageToMove.backgroundColor = .red
self.view.addSubview(imageToMove)
animate(view: imageToMove, altitude: 100, numberOfTime: 10)
}

Swift - How to make animated arrow point in right direction?

I have two buttons which when connected, show a line with an animated arrow to show that they are connected.The issue is the arrow it self is not rotating appropriately to point in the right direction when animated.
How can I rotate the arrow to point in the right direction when moving?
class ViewController: UIViewController {
let line = CAShapeLayer()
let linePath = UIBezierPath()
var triangleImage = UIImage(named: "triangle" )
let startCoordinate = CGRect(x: 100, y: 100, width: 0, height: 0)
let btn1 = UIButton()
let btn2 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
btn1.createRectangleButton(buttonPositionX: 100, buttonPositionY: 100, buttonWidth: 80, buttonHeight: 40, buttonTitle: "", buttonTag: 0)
btn2.createRectangleButton(buttonPositionX: 300, buttonPositionY: 400, buttonWidth: 80, buttonHeight: 40, buttonTitle: "", buttonTag: 1)
view.addSubview(btn1)
view.addSubview(btn2)
let imageView = UIImageView(image: triangleImage)
imageView.frame = CGRect(x:100, y:100, width: 20, height: 20)
imageView.center = self.btn1.center
view.addSubview(imageView)
linePath.move(to: btn1.center)
linePath.addLine(to: btn2.center)
line.path = linePath.cgPath
line.strokeColor = UIColor.red.cgColor
self.view.layer.addSublayer(line)
view.bringSubviewToFront(imageView)
UIView.animate(withDuration: 3, delay: 0.0, options: [.repeat, .curveLinear], animations: {
imageView.center = self.btn2.center
}, completion: nil)
}
note: the code above will look slightly different to the image
Try this rotation transformation
// rotation
imageView.transform = CGAffineTransform(rotationAngle: atan2(btn2.center.y - btn1.center.y, btn2.center.x - btn1.center.x) + CGFloat.pi / 2)
// used triangle image below

Bar Button Item No Corner Radius

I am trying to give this bar button item label a green background and then some corner radius.
utcLbl.frame = CGRect(x: 0, y: 0, width: 100, height: 20)
utcLbl.text = "\(dateFormatter.string(from: Date())) UTC"
utcItem.customView = utcLbl
utcLbl.backgroundColor = UIColor.green
utcLbl.layer.cornerRadius = 20
utcLbl.textAlignment = .center
self.navigationItem.setLeftBarButtonItems([utcItem], animated: true)
here is the code and a picture
any help on getting the green background to have a corner radius.
You need to clipToBounds or masksToBounds
utcLbl.frame = CGRect(x: 0, y: 0, width: 100, height: 20)
utcLbl.text = "\(dateFormatter.string(from: Date())) UTC"
utcItem.customView = utcLbl
utcLbl.backgroundColor = UIColor.green
utcLbl.layer.cornerRadius = 20
utcLbl.textAlignment = .center
utcLbl.layer.masksToBounds = true // Or utcLbl.clipsToBounds = true
self.navigationItem.setLeftBarButtonItems([utcItem], animated: true)

Multiple NSTextField in an NSAlert - MacOS

I am putting together a login dialog using an NSAlert in Swift 4 on MacOS. Here is the code I have so far:
func dialogOKCancel(question: String, text: String) -> (String, String) {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "Login")
alert.addButton(withTitle: "Cancel")
let unameField = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
let passField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
let stackViewer = NSStackView()
stackViewer.addSubview(unameField)
alert.accessoryView = stackViewer
let response: NSApplication.ModalResponse = alert.runModal()
if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
return (unameField.stringValue, passField.stringValue)
} else {
return ("", "")
}
}
This works just fine to show either the unameField or passField when I do alert.accessoryView = passField. But I want to show both fields in the same dialog. As you can see I've experimented with NSStackView (and others), but I have not found a solution to show both elements.
Hope will help you ...
You can use NSStackView and addSubview 2 item : unameField and passField.
But you must set frame for NSStackView and 2 item on NSStackView.
This is my code, you can refer:
let unameField = NSTextField(frame: NSRect(x: 0, y: 2, width: 200, height: 24))
let passField = NSSecureTextField(frame: NSRect(x: 0, y: 28, width: 200, height: 24))
let stackViewer = NSStackView(frame: NSRect(x: 0, y: 0, width: 200, height: 58))
stackViewer.addSubview(unameField)
stackViewer.addSubview(passField)
alert.accessoryView = stackViewer
And this is my result:
my result
My code: https://github.com/nhhthuanck/CustomizeNSAlertMacOS

How to make this UIButton's action operate correctly?

Every time I run this swift playground (on Xcode), I get an error on the last line saying:
'PlaygroundView' cannot be constructed because it has no accessible initialisers.
I also have another error on line 4 saying:
class 'PlaygroundView' has no initializers.
import UIKit
import PlaygroundSupport
class PlaygroundView:UIViewController {
var introImage:UIImageView
var introButton:UIButton
override func loadView() {
print("workspace started running.")
//Main Constant and Variable Declarations/General Setup
let mainView = UIView()
//Main View Modifications & styling
mainView.backgroundColor = UIColor.init(colorLiteralRed: 250, green: 250, blue: 250, alpha: 1)
func addItem(item: UIView) {
mainView.addSubview(item)
}
//Sub-Element Constant and Variable Declarations
introImage = UIImageView(frame: CGRect(x: 87.5, y: -300, width: 200, height: 200))
introImage.layer.borderColor = UIColor.black.cgColor
introImage.layer.borderWidth = 4
introImage.alpha = 0
introImage.transform = CGAffineTransform(rotationAngle: -90.0 * 3.14/180.0)
introImage.image = UIImage(named: "profile_pic.png")
introImage.image?.accessibilityFrame = introImage.frame
introImage.layer.cornerRadius = 100
addItem(item: introImage)
introButton = UIButton(frame: CGRect(x: 87.5, y: 900, width: 200, height: 30))
introButton.alpha = 0
introButton.setTitle("Tap to meet me", for: .normal)
introButton.titleLabel?.font = UIFont(name: "Avenir Book", size: 20)
introButton.backgroundColor = UIColor.black
introButton.layer.cornerRadius = 15
introButton.layer.borderWidth = 5
addItem(item: introButton)
introButton.addTarget(self, action: #selector(self.introButtonAction), for: .touchDown)
UIView.animate(withDuration: 1.5, delay: 1, options: .curveEaseInOut, animations: {
self.introImage.frame = CGRect(x: 87.5, y: 175, width: 200, height: 200)
self.introButton.frame = CGRect(x: 87.5, y: 400, width: 200, height: 30)
self.introImage.transform = CGAffineTransform(rotationAngle: 0.0 * 3.14/180.0)
self.introImage.alpha = 1
self.introButton.alpha = 1
}) { (mainAnimationComped) in
if mainAnimationComped == true {
print("introduction animation comped.")
}
}
}
//User Interface Actions
func introButtonAction() {
print("user interacted with user interface")
}
}
PlaygroundPage.current.liveView = PlaygroundView()
How would I fix this problem?
That's because in Swift, you need to initialize variables before use them in the code. On the other hand, since in this case you set them explicitly in your function, you might declare them as implicitly unwrapped optionals
var introImage:UIImageView!
var introButton:UIButton!
and this should work without changing anything else in your code
Add ? after introImage and introButton
var introImage:UIImageView?
var introButton:UIButton?