Progress of UIprogress view on a label in swift3 - xcode

I am trying to display the progress of the progressview in swift3 on a label using the following code but the progress gets stuck on 10% and doesn't proceeds further.
Please HELP!!!
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(myprogressview),userInfo: nil, repeats: true)
}
func myprogressview(timer : Timer)
{
var count : Int = 0
count += 1
if (count <= 10)
{
Progress1.progress = Float(count)/10.0
progressLabel.text = String.init(format: "%d %%", count*10)
}
timer.invalidate()
}

You have to invalidate the timer in else condition because when function call first time timer invalidate and declaration of count outside the myprogressview() function because each time it become zero
var count : Int = 0
func myprogressview(timer : Timer)
{
count += 1
if (count <= 10)
{
print(count*10)
progress.progress = Float(count)/10.0
progressLable.text = String.init(format: "%d %%", count*10)
} else {
timer.invalidate()
}
}

Related

In SwiftUI how do I animate changes one at a time when they occur in a called method?

Although I get an animation when I tap the button, it's not the animation I want.
The entire view is being replaced at once, but I want to see each element change in sequence. I tried in both the parent view and in the called method. Neither produces the desired result.
(this is a simplified version of the original code)
import SwiftUI
struct SequencedCell: Identifiable {
let id = UUID()
var value: Int
mutating func addOne() {
value += 1
}
}
struct AQTwo: View {
#State var cells: [SequencedCell]
init() {
_cells = State(initialValue: (0 ..< 12).map { SequencedCell(value: $0) })
}
var body: some View {
VStack {
Spacer()
Button("+") {
sequencingMethod(items: $cells)
}
.font(.largeTitle)
Spacer()
HStack {
ForEach(Array(cells.enumerated()), id: \.1.id) { index, item in
// withAnimation(.linear(duration: 4)) {
Text("\(item.value)").tag(index)
// }
}
}
Spacer()
}
}
func sequencingMethod(items: Binding<[SequencedCell]>) {
for cell in items {
withAnimation(.linear(duration: 4)) {
cell.wrappedValue = SequencedCell(value: cell.wrappedValue.value + 1)
// cell.wrappedValue.addOne()
}
}
}
}
struct AQTwoPreview: PreviewProvider {
static var previews: some View {
AQTwo()
}
}
So I want the 0 to turn into a 1, the 1 then turn into a 2, etc.
Edit:
Even though I have accepted an answer, it answered my question, but didn't solve my issue.
I can't use DispatchQueue.main.asyncAfter because the value I am updating is an inout parameter and it makes the compiler unhappy:
Escaping closure captures 'inout' parameter 'grid'
So I tried Malcolm's (malhal) suggestion to use delay, but everything happens immediately with no sequential animation (the entire block of updated items animate as one)
Here's the recursive method I am calling:
static func recursiveAlgorithm(targetFill fillValue: Int, in grid: inout [[CellItem]],
at point: (x: Int, y: Int), originalFill: Int? = nil, delay: TimeInterval) -> [[CellItem]] {
/// make sure the point is on the board (or return)
guard isValidPlacement(point) else { return grid }
/// the first time this is called we don't have `originalFill`
/// so we read it from the starting point
let tick = delay + 0.2
//AnimationTimer.shared.tick()
let startValue = originalFill ?? grid[point.x][point.y].value
if grid[point.x][point.y].value == startValue {
withAnimation(.linear(duration: 0.1).delay(tick)) {
grid[point.x][point.y].value = fillValue
}
_ = recursiveAlgorithm(targetFill: fillValue, in: &grid, at: (point.x, point.y - 1), originalFill: startValue, delay: tick)
_ = recursiveAlgorithm(targetFill: fillValue, in: &grid, at: (point.x, point.y + 1), originalFill: startValue, delay: tick)
_ = recursiveAlgorithm(targetFill: fillValue, in: &grid, at: (point.x - 1, point.y), originalFill: startValue, delay: tick)
_ = recursiveAlgorithm(targetFill: fillValue, in: &grid, at: (point.x + 1, point.y), originalFill: startValue, delay: tick)
}
return grid
}
Further comments/suggestions are welcome, as I continue to wrestle with this.
As mentioned in the comments, the lowest-tech version is probably just using a DisatpchQueue.main.asyncAfter call:
func sequencingMethod(items: Binding<[SequencedCell]>) {
var wait: TimeInterval = 0.0
for cell in items {
DispatchQueue.main.asyncAfter(deadline: .now() + wait) {
withAnimation(.linear(duration: 1)) {
cell.wrappedValue = SequencedCell(value: cell.wrappedValue.value + 1)
}
}
wait += 1.0
}
}
You could use delay(_:) for that, e.g.
func sequencingMethod(items: Binding<[SequencedCell]>) {
var delayDuration = 0.0
for cell in items {
withAnimation(.linear(duration: 4).delay(delayDuration)) {
cell.wrappedValue = SequencedCell(value: cell.wrappedValue.value + 1)
}
delayDuration += 0.5
}
}

How to make NSSpellChecker work with NSDocument?

I have a core data / document-driven macOS app, using Swift and I struggle on combining the out-of-the-box spell checking API with an NSDocument (NSPersistentDocument in my case)
It took me more time than it should take, but this is what I got, mostly guided by this great answer:
class VTDocument: NSPersistentDocument, NSChangeSpelling {
[...]
private let spellchecker = SpellChecker()
#IBAction func showGuessPanel(_ sender: Any?){
spellchecker.startSpellCheck(nodes: Array(db.nodes), tag: 0)
}
#IBAction #objc func changeSpelling(_ sender: Any?){
spellchecker.replace(with: "Test")
}
This is leading me to see the NSSpellChecker.spellingPanel, correctly showing the word to correct. However, the changeSpelling function should be "called" by the panel but is never called. The above spellChecker is a simple wrapper around the NSSpellChecker that keeps the status between function calls.
The SpellChecker class looks like this.
import Cocoa
class SpellChecker {
let checker = NSSpellChecker.shared
let count: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
var nodes = Array<Node> ()
var nodeNr = 0
var stringPos = 0
var range: NSRange = NSRange()
func startSpellCheck(nodes: [Node], tag: Int ) {
self.nodes = nodes
nodeNr = 0
stringPos = 0
continueChecking()
}
func continueChecking(){
if nodes.count == 0 {
return
}
if nodeNr >= nodes.count {
checker.updateSpellingPanel(withMisspelledWord: "")
checker.spellingPanel.orderFront(self)
return
}
if let nodeText = nodes[nodeNr].label {
range = checker.checkSpelling(of: nodeText, startingAt: stringPos, language: nil, wrap: false, inSpellDocumentWithTag: 0, wordCount: count)
if count.pointee > 0 {
stringPos = range.lowerBound
checker.updateSpellingPanel(withMisspelledWord: nodeText[range])
checker.spellingPanel.orderFront(self)
return
}
}
nodeNr = nodeNr + 1
continueChecking()
}
func replace(with: String){
if let nodeText = nodes[nodeNr].label {
let text = nodeText as NSString
text.replacingCharacters(in: range, with: with)
nodes[nodeNr].label = text as String
}
}
}

1-2 seconds delay in NStimer countdown

after looking online for a countDown implementation in swift 2, i couldn't find anyones that works in a countdown way. so i made my own however when it reaches second 01 it takes 2 seconds to become 59. for instance if the timer is on 05:01 it takes 2 seconds of lag or timer freeze, then it becomes 4:59.
it looks weird, i'm a complete beginner so my code is a disaster, here it is:
#IBOutlet var countDown: UILabel!
var currentSeconds = 59
var currentMins = 5
var timer = NSTimer()
#IBAction func start(sender: UIButton) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
}
func updateTime() {
if (currentSeconds > 9) {
countDown.text = "0\(currentMins):\(currentSeconds)"
currentSeconds -= 1
} else if ( currentSeconds > 0) && (currentSeconds <= 9) {
countDown.text = "0\(currentMins):0\(currentSeconds)"
currentSeconds -= 1
} else {
currentMins -= 1
currentSeconds = 59
}
if (currentSeconds == 0) && (currentMins == 0) {
countDown.text = "time is up!"
timer.invalidate()
}
}
#IBAction func stop(sender: AnyObject) {
timer.invalidate()
}
Because you forgot to update the label:
if (currentSeconds > 9) {
countDown.text = "0\(currentMins):\(currentSeconds)"
currentSeconds -= 1
} else if ( currentSeconds > 0) && (currentSeconds <= 9) {
countDown.text = "0\(currentMins):0\(currentSeconds)"
currentSeconds -= 1
} else {
countDown.text = "0\(currentMins):00" // <-- missed this
currentMins -= 1
currentSeconds = 59
}
However, it'd be better if you use NSDateFormatter to format the number of seconds left instead of managing 2 separate variables:
class ViewController: UIViewController, UITextFieldDelegate {
var secondsLeft: NSTimeInterval = 359
var formatter = NSDateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
formatter.dateFormat = "mm:ss"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")!
}
func updateTime()
{
countDown.text = formatter.stringFromDate(NSDate(timeIntervalSince1970: secondsLeft))
secondsLeft -= 1
if secondsLeft == 0 {
countDown.text = "time is up!"
timer.invalidate()
}
}
}

Sigabrt swift Xcode (making a timer)

I'm trying to make a stopwatch, and I can't seem to understand why I am getting a sigabrt with my application. Sorry I didn't know what to include, so I included everything.
var timer = NSTimer()
var minutes: Int = 0
var seconds: Int = 0
var fractions: Int = 0
var startStopWatch: Bool = true
var StopwatchString: String = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if startStopWatch == true {
NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
display.text = "00:00.00"
}
func updateStopWatch() {
fractions += 1
if fractions == 100 {
seconds += 1
fractions = 0
}
if seconds == 60 {
minutes += 1
seconds = 0
}
let fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
let secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
let minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text = StopwatchString
}
I cut and pasted your code into a project and when I tapped the start button the first line of the error message said:
-[My.ViewController updateStopwatch]: unrecognized selector sent to instance 0x7fe0034b3520
That tells you all you need to know. You send the updateStopwatch message to your view controller but it doesn't implement that method. I noticed though that your ViewController does implement an updateStopWatch method. Looks like a basic capitalization error to me.
Always, always, always, read at least the first line of the error message.
Here are some fixes:
+I changed the timer variable initially to nil - it will be saved in the start function.
+Your if statement in the start function looked like this:
if startStopWatch == true
But it should be like this:
if (startStopWatch)
Hope this helpes :)
var timer = nil
var minutes = 0
var seconds = 0
var fractions = 0
var startStopWatch = true
var StopwatchString = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if (startStopWatch == true) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
//I added one thing. If the start button is pushed the text will change to stop. You can change this if you like.
sender.setTitle("Stop", forState: .Normal)
//
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
display.text! = "00:00.00"
}
override func viewDidLoad() {
super.viewDidLoad()
display.text! = "00:00.00"
}
func updateStopWatch() {
fractions++
if (fractions == 100) {
seconds++
fractions = 0
}
if (seconds == 60) {
minutes++
seconds = 0
}
var fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
var secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
var minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text! = StopwatchString
}

NSTimer: floating point value can not be converted to UInt8 because it is greater than UInt8.max

My code returns the error: fatal error: floating point value can not be converted to UInt8 because it is greater than UInt8.max. The code is being called when a button is pressed (in a different swift file) but I know that the problem wasn't with calling the function so I didn't include it.
code:
//starts the timer
func startTimer(sender: AnyObject)
{
if !timer.valid
{
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: sender, selector: Selector(updateTime()), userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
//disp = timerDisplay
}
}
//function that configures the timer
func updateTime()
{
var currentTime = NSDate.timeIntervalSinceReferenceDate()
//find the difference between the current time and the start time
var elapsedTime: NSTimeInterval = currentTime - startTime
//calculate the seconds in elapsed time
let seconds = UInt8(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
///find out the fraction of milliseconds to be displayed
let fraction = UInt8(elapsedTime * 10)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
//let strMinutes = minutes > 9 ? String(minutes):"0" + String(minutes)
let strSeconds = seconds > 9 ? String(seconds):"0" + String(seconds)
let strFraction = fraction > 9 ? String(fraction):"0" + String(fraction)
//display the time left to a string
timerDisplay = "\(strSeconds):\(strFraction)"
}
func stopTimer()
{
timer.invalidate()
}
I would take a different approach at it since you will need to represent NSTimeInterval many times, so you should consider adding a read-only computed property as an extension to your project to return a string representation of it as follow:
extension NSTimeInterval {
var time:String {
return String(format:"%d:%02d:%02d.%02d", Int(self/3600.0), Int((self/60.0) % 60), Int((self) % 60 ), Int(self*100 % 100 ))
}
}
class ViewController: UIViewController {
//declarations
#IBOutlet weak var tappedLabel: UILabel!
#IBOutlet weak var timerLabel: UILabel!
var startTime = NSTimeInterval()
var timer = NSTimer()
//starts the timer
func startTimer(sender: AnyObject) {
if !timer.valid {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: sender, selector: "updateTime", userInfo: nil, repeats:true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
}
// calculates the elapsed time in seconds (Double = NSTimeInterval).extension NStimeInterval
func updateTime() {
//find the difference between the current time and the start time and return a string out of it
// updates the text field
timerLabel.text = (NSDate.timeIntervalSinceReferenceDate() - startTime).time
}
UNint8 can only store values up to 64k (64*1024). If the bucket is full you can't stuff any more into it. Use a larger bucket Int will cope with zillions of digits.

Resources