setting the countdown interval of a WatchKit timer - swift

New to watch development however....
My app gets the user to select a duration for a countdown timer using a slider on one interface controller as shown below:
class game_settings: WKInterfaceController {
#IBOutlet weak var halflength: WKInterfaceSlider!
#IBOutlet weak var halflengthdisplay: WKInterfaceLabel!
#IBOutlet var sethalflengthbutton: WKInterfaceButton!
#IBAction func halfsliderdidchange(value: Float) {
halflengthdisplay.setText("\(value)")
}
override func contextForSegueWithIdentifier(initialhalftogame: String) -> AnyObject? {
// You may want to set the context's identifier in Interface Builder and check it here to make sure you're returning data at the proper times
// Return data to be accessed in ResultsController
return self.halflengthdisplay
}
}
i got this from the following question: Passing data question
then i want the selected interval to be used for the timer on another interface controller as shown below.
class main_game_controller: WKInterfaceController {
#IBOutlet weak var WKTimer: WKInterfaceTimer!//timer that the user will see
var internaltimer : NSTimer?
var ispaused = false
var elapsedTime : NSTimeInterval = 0.0
var StartTime = NSDate()
#IBOutlet var extratime_button: WKInterfaceButton!
#IBOutlet var endgame_button: WKInterfaceButton!
#IBOutlet var sanction_button: WKInterfaceButton!
#IBOutlet var goal_button: WKInterfaceButton!
#IBOutlet var additional_time_timer: WKInterfaceTimer!
#IBOutlet var reset_timer_button: WKInterfaceButton!
#IBOutlet var stop_timer_button: WKInterfaceButton!
#IBOutlet var Start_timer_button: WKInterfaceButton!
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
var halflengthinterval : NSTimeInterval// variable was written to, but never read
// Configure interface objects here.
if let halflength: String = context as? String {
halflengthinterval = Double(halflength)!
}
}
override func willActivate() {
super.willActivate()
}
#IBAction func Start_button_pressed() {
internaltimer = NSTimer.scheduledTimerWithTimeInterval(halflengthinterval, target:self, selector: Selector("timerdone"), userInfo: nil, repeats:false) //use of unfesolved identifier"halflengthinterval"
WKTimer.setDate(NSDate(timeIntervalSinceNow: halflengthinterval))
WKTimer.start()//use of unresolved identifier "halflengthinterval"
}
#IBAction func stop_timer_button_pressed() {
if ispaused{
ispaused = false
internaltimer = NSTimer.scheduledTimerWithTimeInterval(halflengthinterval - elapsedTime, target: self, selector: Selector("timerDone"), userInfo: nil, repeats: false)//use of unresolved identifier 'halflengthinterval'
WKTimer.setDate(NSDate(timeIntervalSinceNow: halflengthinterval - elapsedTime))//use of unresolved identifier 'halflengthinterval'
WKTimer.start()
StartTime = NSDate()
stop_timer_button.setTitle("Pause")
}
else{
ispaused = true
//get how much time has passed before they paused it
let paused = NSDate()
elapsedTime += paused.timeIntervalSinceDate(StartTime)
//stop watchkit timer on screen
WKTimer.stop()
//stop internal timer
internaltimer!.invalidate()
//ui modification
stop_timer_button.setTitle("Resume")
}
}
I was following the answer provided in this question: WKInterface implementation
as you can see in the commented lines above, I'm receiving several errors associated with the variable halflengthinterval. I get the feeling that I'm not correctly passing the interval value between the two interface controllers, but for the life of me i have no idea how to do it.
Could someone please help me in showing
how to pass the value for the timer from the first interface
controller to the second interface controller and
how to correctly set the countdown timer for the length of time selected by the slider in the first interface controller.
Thanks very much!

Let's fix first the error regarding to NSInterval, NSInterval is just a typealis for the type Double:
typealias NSTimeInterval = Double
So the problem you're facing is how to convert a String to a Double and the way is using the Double constructor like in this way:
Double(IntValue)
Regarding how to pass data from two WKInterfaceController you're doing in the right way, but you have one mistake to fix. If you want to pass data from one WKInterfaceController to another WKInterfaceController using segues you can use the contextForSegueWithIdentifier, but in your case you are returning a NSInterval type or Double and then you're trying to cast as an String and this fail in this line of code always:
// Configure interface objects here.
if let halflength: String = context as? String {
halflengthinterval = Double(halflength)!
}
You have to change it to this line instead using the guard statement if you like or using optional-binding, it's up to you:
guard
guard let halflength = context as? Double else {
return
}
self.halflengthinterval = Double(halflength)
optional-binding
if let halflength = context as? Double {
self.halflengthinterval = Double(halflength)
}
I hope this help you.

Related

How do I assign Y to X?

I couldn't figure out how to copy value of variable into another variable in Swift, an example code for this in python would be
def assignVariable():
x=1
y=x
return y
RESULT 1
When I did this it doesn't seem to work in Swift. Is there any solution to this or am I doing something wrong?
Edit: problem is at
var originalCount=countDown
it gave me Use of unresolved identifier 'countDown' but when I assign it literally it works. Here's my swift code
import Cocoa
class MainWindow: NSWindowController {
var hitCount = 0
var started:Bool = false
var timer = 10
var colorList: [NSColor] = [ NSColor.black,NSColor.blue,NSColor.brown,NSColor.cyan,NSColor.darkGray,NSColor.gray,NSColor.green,NSColor.lightGray,NSColor.magenta,NSColor.orange,NSColor.purple,NSColor.red,NSColor.white,NSColor.yellow]
#IBOutlet weak var button1: NSButton!
#IBOutlet weak var scrubber1: NSScrubber!
#IBOutlet weak var display: NSTextField!
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
var countdown=10
var originalCount=countDown
//(countdown,originalCount) = (10,10) //it works if i use this instead
func startGame(){
if(countDown>0 || started==true){
display.stringValue=String(countDown)
countDown-=1
let seconds = 1.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
self.startGame()
}
}else{
display.stringValue="Done "+String(hitCount)+" Taps in " + String(originalCount) + "Tap to RESET"
started=false
countDown=10;
}
}
#IBAction func labelPress(_ sender: Any) {
display.stringValue="__RESET__"
hitCount=0
countDown=10
started=false
}
#IBAction func buttonPressed(_ sender: Any) {
if started==false{
startGame()
}
button1.bezelColor = colorList[Int.random(in: 0..<colorList.count)]
started=true
button1.title=String(hitCount)
hitCount+=1
}
}
You can't initialise one variable with another at the top level in your class. Looking at your code I don't think that originalCount needs to be a property, move it inside startGame() instead and make it a local variable and also use let since it isn't changing
var countdown=10
func startGame(){
let originalCount = countDown
if(countDown>0 || started==true){
...
}

Update NSTextField based on selection in NSPopupButton

I've only been coding for a few weeks so forgive my no-doubt clumsy code.
I have a NSTextField that needs to update based upon a selection from an NSPopupButton
These are my outlets:
#IBOutlet weak var inputText: NSTextField!
#IBOutlet weak var outputText: NSTextField!
#IBOutlet weak var inputBrowseButton: NSButton!
#IBOutlet weak var outputBrowseButton: NSButton!
#IBOutlet weak var codecSelector: NSPopUpButton!
These are my variables:
// derive default path and filename from #IBAction of inputBrowseButton
var defaultUrl: URL!
// derived from default of inputBrowse unless outputBrowse is used
var outputUrl: URL!
// change output path from #IBAction of outputBrowseButton
var outputDirectory: URL!
// change path extension from #IBAction of codecSelector NSPopupButton
var codecChosen: String = ""
// dictionary of arguments to form script for ffmpeg
var ffmpegCodecArg: String = ""
And here is my method to pull all that together:
// construct output path for use in ffmpegArguments dictionary and display in outputText NSTextField
func updateOutputText(defaultUrl: URL?, outputDirectory: URL?) -> String {
if defaultUrl != nil && outputDirectory != nil {
let outputFile = defaultUrl!.deletingPathExtension()
let outputFilename = outputFile.lastPathComponent
outputUrl = outputDirectory!.appendingPathComponent(outputFilename)
outputText.stringValue = outputUrl.path + "\(codecChosen)"
} else if defaultUrl == nil && outputDirectory != nil {
outputText.stringValue = outputDirectory!.path
} else {
outputUrl = defaultUrl!.deletingPathExtension()
outputText.stringValue = outputUrl.path + "\(codecChosen)"
}
return outputText.stringValue
}
Now at the present this function isn't working because I haven't figured out how to call it yet. But that's an issue for another time, not what I'm asking about here.
Previously I was running
outputUrl = defaultUrl!.deletingPathExtension()
outputText.stringValue = outputUrl.path + "\(codecChosen)"
as part of my inputBrowseButton #IBAction method, and I was running
let outputFile = defaultUrl!.deletingPathExtension()
let outputFilename = outputFile.lastPathComponent
outputUrl = outputDirectory!.appendingPathComponent(outputFilename)
outputText.stringValue = outputUrl.path + "\(codecChosen)"
as part of my outputBrowseButton #IBAction method.
Which worked fine, EXCEPT when it came to updating outputText.stringValue when the codecChosen variable was assigned a new value in my #IBAction for the codecSelector NSPopupButton method.
I suspect the problem is that I don't have my outputText NSTextField set up to update when the codecSelector NSPopupButton changes. What do I need to do to make that happen?
How do you set the text in an NSTextField?
Which worked fine, EXCEPT when it came to updating outputText.stringValue when the codecChosen variable was assigned a new value in my #IBAction for the codecSelector NSPopupButton method.
Try using a separate method to change the text.
func changeText(inputString: String, inputField: NSTextField) {
inputField.stringValue = inputString
}
Separating your code more might make it easier for you to keep track of while you are starting out. I am not entirely familiar with macOS, so that method may not work, but try working with the principle of it and see if you can progress.

Swift OSX - Delegate protocol function returns nil, crashes when unwrapping textfield value

I'm working on an OSX app with Swift which makes use of an NSSplitView which holds two view controllers: "TableViewController" and "EntryViewController". I'm using delegates in order to transmit a custom NSObject ("Entry") on click from TableViewController up to the SplitViewController, then back down to the EntryViewController.
My problem is this: When the Entry object is received in the EntryViewController, any attempt to assign its properties to a text field value result in an unexpectedly found nil type error, never mind that the IBOutlets are properly linked, and that it can both print the Entry.property and the textfield string value (provided it is in a different, unrelated function).
I have tried many arrangements to solve this problem, which is why the current configuration might be a bit over-complicated. A delegate relation straight from Table VC to Entry VC caused the same issues.
Is there some way that the IBOutlets are not connecting, even though the view has loaded before the delegate is called? I've read many many articles on delegation—mostly for iOS—and yet can't seem to find the root of my problems. I'll be the first to admit that my grasp of Swift is a little bit piecemeal, so I am open to the possibility that what I am trying to do is simply bad/hacky coding and that I should try something completely different.
Thanks for your help!
TableViewController:
protocol SplitViewSelectionDelegate: class {
func sendSelection(_ entrySelection: NSObject)
}
class TableViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
#IBOutlet var tableArrayController: NSArrayController!
#IBOutlet weak var tableView: NSTableView!
var sendDelegate: SplitViewSelectionDelegate?
dynamic var dataArray = [Entry]()
// load array from .plist array of dictionaries
func getItems(){
let home = FileManager.default.homeDirectoryForCurrentUser
let path = "Documents/resources.plist"
let urlUse = home.appendingPathComponent(path)
let referenceArray = NSArray(contentsOf: urlUse)
dataArray = [Entry]()
for item in referenceArray! {
let headwordValue = (item as AnyObject).value(forKey: "headword") as! String
let defValue = (item as AnyObject).value(forKey: "definition") as! String
let notesValue = (item as AnyObject).value(forKey: "notes") as! String
dataArray.append(Entry(headword: headwordValue, definition: defValue, notes: notesValue))
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.sendDelegate = SplitViewController()
getItems()
print("TVC loaded")
// Do any additional setup after loading the view.
}
// send selection forward to entryviewcontroller
#IBAction func tableViewSelection(_ sender: Any) {
let index = tableArrayController.selectionIndex
let array = tableArrayController.arrangedObjects as! Array<Any>
let obj: Entry
let arraySize = array.count
if index <= arraySize {
obj = array[index] as! Entry
print(index)
print(obj)
sendDelegate?.sendSelection(obj)
}
else {
print("index unassigned")
}
}
}
SplitViewController:
protocol EntryViewSelectionDelegate: class {
func sendSecondSelection(_ entrySelection: NSObject)
}
class SplitViewController: NSSplitViewController, SplitViewSelectionDelegate {
var delegate: EntryViewSelectionDelegate?
#IBOutlet weak var mySplitView: NSSplitView!
var leftPane: NSViewController?
var contentView: NSViewController?
var entrySelectionObject: NSObject!
override func viewDidLoad() {
super.viewDidLoad()
// assign tableview and entryview as child view controllers
let story = self.storyboard
leftPane = story?.instantiateController(withIdentifier: "TableViewController") as! TableViewController?
contentView = story?.instantiateController(withIdentifier: "EntryViewController") as! EntryViewController?
self.addChildViewController(leftPane!)
self.addChildViewController(contentView!)
print("SVC loaded")
}
func sendSelection(_ entrySelection: NSObject) {
self.delegate = EntryViewController() //if this goes in viewDidLoad, then delegate is never called/assigned
entrySelectionObject = entrySelection
print("SVC:", entrySelectionObject!)
let obj = entrySelectionObject!
delegate?.sendSecondSelection(obj)
}
}
And Finally, EntryViewController:
class EntryViewController: NSViewController, EntryViewSelectionDelegate {
#IBOutlet weak var definitionField: NSTextField!
#IBOutlet weak var notesField: NSTextField!
#IBOutlet weak var entryField: NSTextField!
var entryObject: Entry!
override func viewDidLoad() {
super.viewDidLoad()
print("EVC loaded")
}
func sendSecondSelection(_ entrySelection: NSObject) {
self.entryObject = entrySelection as! Entry
print("EVC:", entryObject)
print(entryObject.headword)
// The Error gets thrown here:
entryField.stringValue = entryObject.headword
}
}
You don't need a delegate / protocol since there is a reference to EntryViewController (contentView) – by the way the instance created with EntryViewController() is not the instantiated instance in viewDidLoad.
Just use the contentView reference:
func sendSelection(_ entrySelection: NSObject) {
contentView?.sendSecondSelection(entrySelection)
}

Initializing class object swift 3

i have a problem while I'm initializing object of some class. What is wrong with that? (I can upload all my code but it is large if needed)
Edit:
My view controller code:
import UIKit
class ViewController: UIViewController{
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var answerStackView: UIStackView!
// Feedback screen
#IBOutlet weak var resultView: UIView!
#IBOutlet weak var dimView: UIView!
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var feedbackLabel: UILabel!
#IBOutlet weak var resultButton: UIButton!
#IBOutlet weak var resultViewBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var resultViewTopConstraint: NSLayoutConstraint!
var currentQuestion:Question?
let model = QuizModel()
var questions = [Question]()
var numberCorrect = 0
override func viewDidLoad() {
super.viewDidLoad()
model.getQuestions()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setAll(questionsReturned:[Question]) {
/*
// Do any additional setup after loading the view, typically from a nib.
// Hide feedback screen
dimView.alpha = 0
// Call get questions
questions = questionsReturned
// Check if there are questions
if questions.count > 0 {
currentQuestion = questions[0]
// Load state
loadState()
// Display the current question
displayCurrentQuestion()
}
*/
print("Called!")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
My QuizModel code:
import UIKit
import FirebaseDatabase
class QuizModel: NSObject {
override init() {
super.init()
}
var ref:FIRDatabaseReference?
var test = [[String:Any]]()
var questions = [Question]()
weak var prot:UIPageViewControllerDelegate?
var first = ViewController()
func getQuestions(){
getRemoteJsonFile()
}
func pars(){
/*let array = test
var questions = [Question]()
// Parse dictionaries into Question objects
for dict in array {
// Create question object
let q = Question()
// Assign question properties
q.questionText = dict["question"] as! String
q.answers = dict["answers"] as! [String]
q.correctAnswerIndex = dict["correctIndex"] as! Int
q.module = dict["module"] as! Int
q.lesson = dict["lesson"] as! Int
q.feedback = dict["feedback"] as! String
// Add the question object into the array
questions += [q]
}
*/
//Protocol setAll function
first.setAll(questionsReturned: questions)
}
func getRemoteJsonFile(){
ref = FIRDatabase.database().reference()
ref?.child("Quiz").observeSingleEvent(of: .value, with: { (snapchot) in
print("hey")
let value = snapchot.value as? [[String:Any]]
if let dict = value {
self.test = dict
self.pars()
}
})
}
This isn't my all code but I think that is the most important part. In QuizModel code I'm reskining my code from getting json file to get data from firebase so you can see names of functions like 'getRemoteJSONFile' and in parse function parsing json but it isn't an issue of my problem I think
It looks like you're trying to initialize a constant before you've initialized the viewController it lives in. Your have to make it a var and initialize it in viewDidLoad (or another lifecycle method), or make it a lazy var and initialize it at the time that it's first accessed.
The problem boils down to the following:
class ViewController ... {
let model = QuizModel()
}
class QuizModel ... {
var first = ViewController()
}
The variable initializers are called during object initialization. When you create an instance of ViewController, an instance of QuizModel is created but its initialization creates a ViewController which again creates a new QuizModel and so on.
This infinite cycle will sooner or later crash your application (a so called "stack overflow").
There is probably some mistake on your side. Maybe you want to pass the initial controller to QuizModel, e.g.?
class ViewController ... {
lazy var model: QuizModel = {
return QuizModel(viewController: self)
}
}
class QuizModel ... {
weak var viewController: ViewController?
override init(viewController: ViewController) {
self.viewController = viewController
}
}
Also make sure you are not creating ownership cycles (that's why I have used weak).
In general it's a good idea to strictly separate your model classes and your UI classes. You shouldn't pass view controllers (or page view controller delegate) to your model class. If you need to communicate with your controller, create a dedicated delegate for that.

Need button to change labels when clicked

i-m trying to learn how to code and working on my first ever app. it is some gym app. and i want to have a timer and a label to be changed after presing a button.
This is my code until now but i cant figure it out how to display the next label and the next timer to countdown after presing the button.
class _daysday1ViewController: UIViewController {
#IBOutlet weak var exerciseNameLabel: UILabel!
#IBOutlet weak var btnStart: UIButton!
#IBOutlet weak var exercisePicture: UIImageView!
#IBOutlet weak var exerciseDescriptionLabel: UILabel!
#IBOutlet weak var timerLabel: UILabel!
#IBAction func nextButtonTapped(sender: AnyObject) {
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
count = countList[0]
}
var exerciseIndex = 0
var exerciseList = ["12 reps at 80% of capicity" , " 10 reps at 90%" , "7 to 10 reps at full capacity"]
var countList = [4 , 6 ,8]
var count = 1
override func viewDidLoad() {
super.viewDidLoad()
}
func update() {
if(count >= 0) {
timerLabel.text = String(count--)
self.btnStart.enabled = false
return
}
btnStart.enabled = true
exerciseDescriptionLabel.text = exerciseList[0]
}
`
Your timer will go away almost as soon as it's created, because you declared the timer var within the nextButtonTapped function. That function finishes quickly, and your timer goes away when it finishes.
To fix this, declare the timer var at the class level, the same place you declared the count var, like this:
var timer: NSTimer?
Declaring it as an optional (?) allows it be nil until the next button is tapped.
To create it, simply change the line of code in nextButtonTapped to begin with this:
self.timer = ...
Instead of this:
var timer = ...
In your func nextButtonTapped() method, you can include the code for this. The #IBAction will make this method fire every time the button is touched, so if you need variables to change with this action simple outline what you want to change between the {};
example:
var currentExercise = 0
#IBAction func nextButtonTapped() {
btnStart.textLabel.text = "New Text To Display"
currentExercise += 1
exerciseDescriptionLabel.text = exerciseList[currentExercise]
}