Why array's append method cannot be used in viewController? - swift

I am beginner of swift. I tried to use array's append method in my code but it doesn't work. How should I implement the array correctly?
The error messages:
Swift Compiler Error Group
ViewController.swift:16:5: Expected declaration
ViewController.swift:11:7: In declaration of 'ViewController'
I tried to use array's append method in my code but it doesn't work.
import UIKit
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
dices.append("Hi") //Error: Expected declaration
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
I expect I can add "hi" into the array dices.

You should call the append inside a function after the vc is fully initated
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
dices.append("Hi") // e.x here
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
Or replace
var dices : [String] = []
with
var dices = ["Hi"]

SH_Khan is right. I'll explain why though.
When defining a class, the first level of indentation is only for its methods and properties, aka func, var, and let. (You can also define other classes/structs/enums in there too)
Calling those functions or system functions like Array.append() or print("dog sweat") must happen inside of another function. The reason why is that your application's live logic is literally just functions all the way down. No function gets called unless it's inside of another function first. (The only exceptions are Swift's quick and dirty initializations like setting a default value to a var outside of an init() { } or another function.)
A dog doesn't wake up from its nap unless you make some noise. It won't do it on its own. (crappy metaphor, but yeah)
I hope that made any sense.

Related

Swift - Can't access struct properties

Learning swift but a little confused as to the below. I have struct created and instantiated, but I cannot access the property of the struct inside of my view controller class unless the struct is inside one my my class methods. See below, why would this be the case?
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
//create new weather manager struct
var weatherManager = WeatherManager()
//can't access property here, but I can access it inside of functions within this
//class, see below under viewDidLoad()
weatherManager.delegate = self
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//can access property here
weatherManager.delegate = self
}
The problem isn't where the WeatherManager object is declared and created. The problem is that this line:
weatherManager.delegate = self
is a command (technically a statement), not a declaration. (The line above it is a declaration, one that happens to also set the weatherManager property's default value). This is a pretty universal rule in most languages in the C++/Java family -- see short C++ example below. A command (statement) must be inside some method (or function, in non-OOP programming), not at the top level of a file or class. In Swift, actions like setting an object's delegate would typically go in the view controller's viewDidLoad.
int x = 0; // legal: declaring a global variable
x = x + 42; // NOT legal: this is a statement, not a declaraiton
int main()
{
x = x + 42; // legal: now we're inside a function
return 0;
}

Variable in label Name - Swift

Is it possible to use a variable in a label name.
For example, my label is called button1text and I have a variable var x = 1.
Is there a way to do thisbutton(x)text?
No. You can not have variable name in identifier.
Variable names are evaluated at compile time, so no, it's not possible (at runtime).
Alternatively use an array or assign tags to the labels and get the label with viewWithTag
You can use reflection with Mirror. The capabilities are very limited in the context you want but you can try something like:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
#IBAction func didTapGoButton(_ sender: Any) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let v = child.label, v == "button2" {
(child.value as! UIButton).titleLabel?.text = "changed"
}
}
}
}

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.

NSTextField updated randomly through delegate in Swift

I have a very strange behavior on a NSTextField.
I update the value of the NSTextField through a delegate. Sometimes it gets updated and sometimes not. I issued a print statement before to ensure that I have the correct value. What the print statement shows and what is being displayed on the NSTextField is different.
Any idea what could be the root cause ?
import Cocoa
var mtserialport = MTSerialHandler()
class ManualViewController: NSViewController, MTSerialHandlerDelegate {
#IBOutlet var txtStatus : NSTextField!
#IBOutlet var txtQueue : NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
init_ctrl()
// Delegates
mtserialport.delegate = self
}
func init_ctrl() {
self.txtQueue.stringValue = "0"
}
// This is the function called from a delegate
// mt_serialport delegate
// print shows updateQueue:0 or 1, textQueue would stay to a previous value. i.e:3
func updateQueue(qu: UInt) {
print("updateQueue:" + String(qu))
self.txtQueue.stringValue = String(qu)
}
}

Initializing a variable initialized from IBoutlet variables for a view controller?

I am trying to initialize the variable deckEditorModel using termAndDefTable and copyNPasteTable as inputs. I don't want deckEditorModel to be an optional variable as a know that I would not be able to use that tactic in C. I tried putting deckEditorModel in the init methods for the view controller but it seems that the required termNDefTable: termAndDefTable, otherTable: OtherTable are not available to use as imputs during the init as they are IBOutlets. How would someone go about this
import Cocoa
class DeckEditorViewController: NSViewController {
#IBOutlet weak var importToolsView: NSView!
#IBOutlet weak var termAndDefTable: NSTableView!
#IBOutlet weak var otherTable: NSTableView!
var deckEditorModel:DeckEditorModel?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
self.view.wantsLayer = true
}
override func awakeFromNib() {
deckEditorModel = DeckEditorModel(termNDefTable: termAndDefTable, otherTable: otherTable)
}
I tried adding it to the viewDidLoad but apparently that is to late to define deckEditorModel. The below error dissappears when deckEditorModel is an optional
You could initialize the variable lazily
lazy var deckEditorModel : DeckEditorModel = {
return DeckEditorModel(termNDefTable: self.termAndDefTable, otherTable: self.otherTable)
}()
Place
deckEditorModel = DeckEditorModel(termNDefTable: termAndDefTable, otherTable: otherTable)
In viewDidLoad, then the outlets will have been set.