NSTextField updated randomly through delegate in Swift - 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)
}
}

Related

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

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.

Reach two IBOutlets of NSTextField at once

I have a login-page with two NSTextFields, so I created two #IBOutlets in my viewcontroller. For some styling I have to call both of them. I want to give them the same properties, but I dont want to call them individual. So how can I reach them at once to give them a property like inputFields.isBordered = true
For iOS swift provides #IBOutletCollection but for MacOS not.
#IBOutlet weak var emailInput: NSTextField!
#IBOutlet weak var passwordInput: NSTextField!
override func viewWillAppear() {
emailInput.isBordered = true
passwordInput.isBordered = true
}
So I want two call them like inputFields.isBordered = true and not individual.
You can do this in many different ways. For example:
func modifyTextFields(_ closure: (NSTextField) -> Void) {
closure(emailInput)
closure(passwordInput)
}
// usage:
modifyTextFields { $0.isBordered = true }
Alternatively,
var textFields: [NSTextField] { // this kind of works like an outlet collection
return [emailInput, passwordInput]
}
// usage:
textFields.forEach { $0.isBordered = true }

tvOS - preferredFocusEnvironments not working

Because I needed a UINavigationController inside another UINavigationController (and this is not possible by default), I created a UIViewController that acts as a UINavigationController, but dit does not subclass from UINavigationController.
The second NavigationController (the one that does to subclass UINavigationController), presents (depending on the ViewModel's state) a controller.
This is the custom NavigationController:
class OnboardingViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var containerView: UIView!
// MARK: - Internal
private var viewModel: OnboardingViewModel = OnboardingViewModel()
// MARK: - View flow
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = true
navigateToNextFlow()
}
override var preferredFocusEnvironments: [UIFocusEnvironment] {
switch viewModel.state {
case .recommendations: return recommendationsController.preferredFocusEnvironments
default: return super.preferredFocusEnvironments
}
}
// MARK: - Handlers
var navigateToNextHandler: (() -> Void)?
// MARK: - Controllers
private var recommendationsController: OnboardingRecommendationsViewController {
let controller = UIViewController.instantiate(from: "Onboarding Recommendations") as OnboardingRecommendationsViewController
controller.navigateToNextHandler = { [unowned self] in
self.viewModel.state = .done
self.navigateToNextFlow(animated: true)
}
return controller
}
// MARK: - Navigation
private func navigateToNextFlow(animated: Bool = false) {
switch viewModel.state {
case .recommendations:
add(child: recommendationsController, to: containerView)
case .done:
viewModel.finish()
navigateToNextHandler?()
}
updateFocusIfNeeded()
setNeedsFocusUpdate()
}
}
This is the childViewController:
class OnboardingRecommendationsViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var onOffButton: UIButton!
#IBOutlet weak var finishButton: UIButton!
// MARK: - Internal
fileprivate let viewModel: OnboardingRecommendationsViewModel = OnboardingRecommendationsViewModel()
// MARK: - View flow
override func viewDidLoad() {
super.viewDidLoad()
setupLabels()
setupOnOffButton()
}
// MARK: - Handlers
var navigateToNextHandler: (() -> Void)?
// MARK: - Focus
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [finishButton, onOffButton]
}
}
The finishButton is beneath the onOffButton in the storyboard. I'm trying to set the initial focus on the finishButton instead of the onOffButton. But the user can focus the onOffButton if he wants.
Whatever I try, it just doesn't work. The preferredFocusEnvironments gets called, but the focus of the buttons stays in the wrong order.
What am I doing wrong?
Sorry for the late answer. It turned out that the viewController I was pushing, defined was as let, so I pushed a few instances over themselves, and that's why it seems that the preferredFocusEnvironments was not working. I actually saw a new instance of the ViewController with another initial focus order. Changing the variable declaration of the viewController from let to lazy var did the trick. So, in the end, it had really nothing to do with preferredFocusEnvironments not working. But thanks for the input!
Did you try like this?
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [finishButton]
}
Or you can disable userInteraction for onOffButton until finishButton gets focused. (Not a good solution though)
You should set restoresFocusAfterTransition = false to avoid the default behavior. And then, in preferredFocusEnvironments return the view you want to focus

App crash when successfully passing data

I have a simple app. A table view passing user information from 1 view controller to another. It successfully prints out my user object and I can see the data in the console, however when I go to extract the data from the object into my view it does not display
#IBOutlet weak var chatPartnerLabel: UILabel!
var user: User? {
didSet {
print(user) //This prints out all information fine
chatPartnerLabel.text = user?.firstName //This crashes the app because chatPartnerLabel.text is nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
chatPartnerLabel.text = user?.firstName //This comes up as nil
}
I have also tried hardcoding the label to see if it works properly
chatPartnerLabel.text = "My text" //Changes the label to My Text
I have also tried putting user?.firstName in viewDidAppear and viewWillAppear both display an empty label.
As you can see in the console my user is there with data. The crash I get when attempting to set is coming up as nil
What am I doing wrong to successfully change my chatPartnerLabel to either the name or firstname?
class User {
var firstName = "fn"
}
///uiviewcontroller:
#IBOutlet weak var chatPartnerLabel: UILabel!
var user: User? {
didSet {
print(user) //This prints out all information fine
chatPartnerLabel.text = user?.firstName //This crashes the app
}
}
both:
override func viewDidLoad() {
super.viewDidLoad()
user = User()
and:
override func viewDidLoad() {
super.viewDidLoad()
chatPartnerLabel.text = user?.firstName
work! I've tested!
We need more information and correct callstack. May be chatPartnerLabelis nil while unwrapping.
if you are setting user in segue or before pushing/presenting controller, it will crash, because label is not instantiated yet. That's why you have to rewrite your code to:
#IBOutlet weak var chatPartnerLabel: UILabel! {
didSet {
print(user?.firstName) // just for check
chatPartnerLabel.text = user?.firstName
}
}
var user: User? {
didSet {
print(user) //This prints out all information fine
}
}
This works Swift 3. I added a Boolean control to know when viewDidLoad is called.
var user: (firstName : String, lastName : String, controlView : Bool)? {
didSet {
if (self.user?.controlView)! {
print(user!)
chatPartnerLabel.text = user?.firstName
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
user?.controlView = true
}

PopUpPicker - does not conform to protocol

I am very new to Swift and programming in general.
I am trying to add a Pop Up Picker on a textfield and when the user selects the item from the picker, they can press OK with that item displayed in the textfield and the PopUp disappear.
I have successfully implemented this with a Pop Up Date Picker as I have used this from GutHub successfully. I thought it would be easy to mimic this code for my Pop Up Picker which has proven to be more difficult than expected.
I have a sepeate XIB file which holds the View with the Picker and OK Button. I then have 2 swift files one for the PopViewController and the other for the PopPicker.
Not even sure if this code is correct but the error I am getting is that my Picker does not conform to protocol. Code is below for both files.
PopEngineViewController
import UIKit
protocol EnginePickerViewControllerDelegate : class {
func enginePickerVCDismissed(string: UITextField?)
}
class PopEngineViewController: UIViewController {
#IBOutlet weak var container: UIView!
#IBOutlet weak var enginePicker: UIPickerView!
weak var delegate : EnginePickerViewControllerDelegate?
override convenience init() {
self.init(nibName: "PopEnginePicker", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidDisappear(animated: Bool) {
self.delegate?.enginePickerVCDismissed(nil)
}
}
and PopEnginePicker
import UIKit
public class PopEnginePicker : NSObject, UIPopoverPresentationControllerDelegate, EnginePickerViewControllerDelegate {
public typealias PopEnginePickerCallback = (forTextField : UITextField)->()
var enginePickerVC : PopEngineViewController
var popover : UIPopoverPresentationController?
var textField : UITextField!
var dataChanged : PopEnginePickerCallback?
var presented = false
var offset : CGFloat = 8.0
public init(forTextField: UITextField) {
enginePickerVC = PopEngineViewController()
self.textField = forTextField
super.init()
}
public func pick(inViewController : UIViewController, dataChanged : PopEnginePickerCallback) {
if presented {
return // we are busy
}
enginePickerVC.delegate = self
enginePickerVC.modalPresentationStyle = UIModalPresentationStyle.Popover
enginePickerVC.preferredContentSize = CGSizeMake(500,208)
popover = enginePickerVC.popoverPresentationController
if let _popover = popover {
_popover.sourceView = textField
_popover.sourceRect = CGRectMake(self.offset,textField.bounds.size.height,0,0)
_popover.delegate = self
self.dataChanged = dataChanged
inViewController.presentViewController(enginePickerVC, animated: true, completion: nil)
presented = true
}
}
func adaptivePresentationStyleForPresentationController(PC: UIPresentationController!) -> UIModalPresentationStyle {
return .None
}
}
Not even sure if I am going down the complete wrong path however I want it to look like the below as I have done with the date picker as it shows in the link below:
http://coding.tabasoft.it/ios/a-simple-ios8-popdatepicker/