Reach two IBOutlets of NSTextField at once - swift

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 }

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.

How do I check if x is in a variable in classes within a list?

I have a list called mainframe which holds classes. I want to check before adding a new username; if newusername is in mainframe.usernames perform adding the new username in.
pretty much something like this:
import UIKit
class addNewPassword: UIViewController {
var homeVC = Home()
#IBOutlet weak var createHolderItem: UITextField!
#IBOutlet weak var createHolderUsername: UITextField!
#IBOutlet weak var createHolderPassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func savePasswordButton(_ sender: Any) {
let holder = Holder()
holder.item = createHolderItem.text!
holder.username = createHolderUsername.text!
holder.password = createHolderPassword.text!
}
if mainframe.contains(where: { $0.username == holder.username }) {
print("test")
}
else {
homeVC.mainframe.append(holder)
homeVC.tableView.reloadData()
navigationController?.popViewController(animated: true)
}
}
I pretty much want to run a loop, within an if statement. Or am I approaching it the wrong way?
I'm new to programming, did online tutorials and trying to write my first iOS app for my aunt.
if mainframe.usernames.contains(holder.username) {
...
Use contains :
if mainframe.usernames.contains(holder.username) {
...
}

How can I refer to self during initialization when using IBOutlets for UIViews?

I have this custom UITableViewCell class backing my cells in a table view (I learned this approach from a talk by Andy Matuschak who worked at Apple on the UIKit team).
In the project I'm trying to apply this to now, I'm having a problem initializing the class because of a couple of #IBOutlets that are linked to UIView elements that won't ever have values like the UILabels get upon initialization:
class PublicationTableViewCell: UITableViewCell {
#IBOutlet weak var publicationTitle: UILabel!
#IBOutlet weak var authorName: UILabel!
#IBOutlet weak var pubTypeBadgeView: UIView!
#IBOutlet weak var pubTypeBadge: UILabel!
#IBOutlet weak var topHighlightGradientStackView: UIStackView!
#IBOutlet weak var bottomBorder: UIView!
struct ViewData {
let publicationTitle: UILabel
let authorName: UILabel
let pubTypeBadge: UILabel
}
var viewData: ViewData! {
didSet {
publicationTitle = viewData.publicationTitle
authorName = viewData.authorName
pubTypeBadge = viewData.pubTypeBadge
}
// I have #IBOutlets linked to the views so I can do this:
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if isSelected || isHighlighted {
topHighlightGradientStackView.isHidden = true
bottomBorder.backgroundColor = UIColor.lightGrey1
} else {
topHighlightGradientStackView.isHidden = false
}
}
}
extension PublicationTableViewCell.ViewData {
init(with publication: PublicationModel) {
// Xcode complains here about referring to the properties
// on the left before all stored properties are initialized
publicationTitle.text = publication.title
authorName.text = publication.formattedAuthor.name
pubTypeBadge.attributedText = publication.pubTypeBadge
}
}
I then initialize it in cellForRow by passing in a publication like this:
cell.viewData = PublicationTableViewCell.ViewData(with: publication)
Xcode is complaining that I'm using self in init(with publication: PublicationModel) before all stored properties are initialized which I understand, but I can't figure out how to fix.
If these weren't UIView properties I might make them optional or computed properties perhaps, but because these are IBOutlets, I think they need to be implicitly unwrapped optionals.
Is there some other way I can get this to work?
First of all:
struct ViewData {
let publicationTitle: String
let authorName: String
let pubTypeBadge: NSAttributedString
}
Then simply:
extension PublicationTableViewCell.ViewData {
init(with publication: PublicationModel)
publicationTitle = publication.title
authorName = publication.formattedAuthor.name
pubTypeBadge = publication.pubTypeBadge
}
}
It's a data model, it shouldn't hold views, it should hold only data.
Then:
var viewData: ViewData! {
didSet {
publicationTitle.text = viewData.publicationTitle
authorName.text = viewData.authorName
pubTypeBadge.attributedString = viewData.pubTypeBadge
}
}
However, I think it would be simpler if you just passed PublicationModel as your cell data. There is no reason to convert it to another struct.
var viewData: PublicationModel! {
didSet {
publicationTitle.text = viewData.publicationTitle
authorName.text = viewData.authorName
pubTypeBadge.attributedString = viewData.pubTypeBadge
}
}

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)
}
}

Delegation: How to Pass a Property Between Custom Views

In the code below, which (I hope) includes all that's relevant to my question, a mouseEntered/-Exited event in ChangerView is supposed to change the display in ChangingView. (ChangerView and ChangingView are displayed side-by-side and share a view controller.) As an OOP newbie, though, I'm seriously missing something about how to set up delegation between these views. Here's ChangerView (in which DoThis?.show = nil, despite that I thought I was setting it to true or false):
import Cocoa
protocol DoThis { var show: Bool { get set } }
class ChangerView: NSView {
var changeDelegate: DoThis?
// Set up for mouseEntered/-Exited
override func mouseEntered(theEvent: NSEvent) { DoThis?.show = true }
override func mouseExited(theEvent: NSEvent) { DoThis?.show = false }
}
And here's changing view:
import Cocoa
class ChangingView: NSView, DoThis {
var show: Bool = false { didSet { needsDisplay = true } }
// Draw into the view
override func drawRect(dirtyRect: NSRect) {
switch show {
case true: // Display setup contingent on show = true
case false: // Display setup contingent on show = false
}
// Draw contingent display
}
}
As I understand things, views should do their own basic display work, and view controllers should handle model-related and higher-level display changes. For that reason, and to keep things simple, I want ChangerView and ChangingView to communicate directly. Unfortunately, I couldn't find any explanations about delegation close enough to this situation—at least not that I could understand.
What am I missing (besides a properly functioning brain)?
Thanks!
It looks like there are two issues.
In your ChangerView class, you should be using the delegate to set the show variable, like this:
import Cocoa
protocol DoThis { var show: Bool { get set } }
class ChangerView: NSView {
var changeDelegate: DoThis?
// Set up for mouseEntered/-Exited
override func mouseEntered(theEvent: NSEvent) { changeDelegate?.show = true }
override func mouseExited(theEvent: NSEvent) { changeDelegate?.show = false }
}
You may want to make the delegate variable weak to prevent reference cycles
The other issue is you've forgot the step where you assign the delegate. I (and I think everyone else) forget this often. Once you get used to setting up delegates you'll remember to check for it if things don't work at first.
So at some point you need to set the changeDelegate var to an instance of the ChangingView class (this is often done in the viewDidLoad() function.
It will look something like this:
class SomeViewController: UIViewController {
#IBOutlet weak var SomeChangerView: ChangerView!
#IBOutlet weak var SomeChangingView: ChangingView!
override func viewDidLoad() {
super.viewDidLoad()
SomeChangerView.changerDelegate = SomeChangingView
}