How to access properties outside of an enum - swift

How can I access my class properties from within an enum contained in this class?
But as soon as I instantiate multiple MyClass, the _toggledOn is the same for all instances.
I hope there is some clean and swifty workaround I don't know. Anyways thank you in advance!
#IBDesignable
class MyClass: UIView, SomeProtocols {
// MARK: - ENUM
enum ToggleState {
case on
case off
var color: UIColor {
switch self {
case .on:
return _onColor
default:
return _offColor
}
}
}
// MARK: - STATICS
private static var _onColor: UIColor = #colorliteral
private static var _offColor: UIColor = #colorliteral
// MARK: - IBSTUFF
#IBInspectable var toggledOffColor: UIColor = #colorliteral {
didSet {
MyClass._offColor = toggledOffColor
updateUI()
}
}
#IBInspectable var toggledOnColor: UIColor = #colorliteral {
didSet {
MyClass._onColor = toggledOnColor
updateUI()
}
}
#IBOutlet weak var background: UIView!
#IBAction func buttonPressed(_ sender: UIButton) {
toggleState = toggleState == .off ? .on : .off
}
// MARK: - PROPERTIES
var toggleState: ToggleState = .off {
didSet { toggle() }
}
// MARK: - METHODS
func updateUI() {
background.backgroundColor = toggleState.color
background.layer.addShadow()
}
func toggle() {
background.backgroundColor = toggleState.color
}
}

Your _onColor and _offColor are static, so there will not be a separate _onColor and _offColor for each instance of your class.\
You should make _onColor and _offColor non-static, and remove the color property of ToggleState, and instead add such a method in your class:
func color(forToggleState state: ToggleState) {
switch state {
case .on:
return _onColor
case .off:
return _offColor
}
}
Instead of toggleState.color, you should write:
color(forToggleState: toggleState)
color shouldn't be a property of ToggleState because to compute it, you need information from another object.

Related

how to init() a swift view controller properly?

I'm attempting to initialize this ViewController class. I am not using the MVC design strategy so ignore the bad conventions used (if any).
How do I initialize this class properly?
Error: 'required' initializer 'init(coder:)' must be provided by subclass of 'UIViewController'
Context: This is a calculator app that when any of the buttons are pressed. It will go find the senders title and simply put if one of the three vars are nil, it will store it in that optional.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var answerLabel: UILabel!
//global vars for all funcs
var selection1: Int? {
didSet { answerLabel.text = String(selection1!) }
}
var selection2: String? {
didSet { answerLabel.text = selection2! }
}
var selection3: Int? {
didSet { answerLabel.text = String(selection3!) }
}
var answer: Int {
didSet { answerLabel.text = String(answer) }
}
init() {
}
#IBAction func touchButton(_ sender: UIButton) {
if selection1 == nil {
selection1 = Int(sender.currentTitle!)
print("Selection set in first pos.")
} else if selection2 == nil {
selection2 = sender.currentTitle
} else if selection3 == nil {
selection3 = Int(sender.currentTitle!)
} else {
calculate(firstNum: selection1!, operation: selection2!, secondNum: selection3!)
}
}
func calculate(firstNum: Int, operation: String, secondNum: Int) {
switch operation {
case "+":
answer = firstNum + secondNum
case "-":
answer = firstNum - secondNum
case "x":
answer = firstNum * secondNum
case "/":
answer = firstNum / secondNum
default:
answerLabel.text = "Something went wrong!"
}
}
}
Initialization depends on a couple of condition.
If you are using storyboard, you can just remove the init and your VC will have default initializer. Make sure either all of your properties have default value or they are optional.
If you are using xib or just creating view programmatically you can have custom convenience initializer where you pass some extra data this way.
class MyViewController: ViewController {
var answer: Int
convenience init(answer: Int) {
self.init()
self.answer = answer
// Do other setup
}
}
Your controller is being instantiated from the storyboard. A safe place to configure initial views is during the controller's call to viewDidLoad, ie:
override func viewDidLoad() {
super.viewDidLoad()
// configure your views and subviews here
}

Generic view generator unable to infer type swift

have some struggle get my generic view generator working.
the idea behind is to have a array of different concrete view settings.
to generate the resulting views ive made the function in the pastebin below.
but it cannot infer the right type for the cell.
can anybody gibe me a hint ?
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
// Protocols
protocol SettingType {
var color: UIColor { get }
}
protocol CellType {
func configure(with setting: SettingType)
}
// Concrete Settings
final class Concrete1Setting: SettingType {
var color: UIColor = .blue
}
final class Concrete2Setting: SettingType {
var color: UIColor = .green
}
// Concrete Cells
final class Concrete1Cell: UIView, CellType {
func configure(with setting: SettingType) {
print("Configured Cell 1")
self.backgroundColor = setting.color
}
}
final class Concrete2Cell: UIView, CellType {
func configure(with setting: SettingType) {
print("Configured Cell 2")
self.backgroundColor = setting.color
}
}
// Generic generator
func makeConcreteCell<S: SettingType, T: CellType>(_ setting: S) -> T {
switch setting {
case is Concrete1Setting:
return Concrete1Cell() as! T
case is Concrete2Setting:
return Concrete2Cell() as! T
default: fatalError()
}
}
// Test
var cells: [CellType] = [Concrete1Cell(), Concrete2Cell()]
let settings: [SettingType] = [Concrete1Setting(), Concrete2Setting()]
for setting in settings {
cells.append(makeConcreteCell(setting)) // CompileError: Generic parameter 'S' could not be inferred
}
// expect array equal to [Concrete1Cell, Concrete2Cell]
You don't need the generic function for makeConcreteCell. Just have it take and return the prototypes:
func makeConcreteCell(_ setting: SettingType) -> CellType {
switch setting {
case is Concrete1Setting:
return Concrete1Cell()
case is Concrete2Setting:
return Concrete2Cell()
default: fatalError()
}
}
Generics are used when you want the compiler to write a specific function for each specific type. That isn't what you need here. The one function works.
You want to use the CellTypes as UIViews.
Create a base class called CellTypeClass that is a UIView and adopts the CellType protocol:
// Protocols
protocol SettingType {
var color: UIColor { get }
}
protocol CellType {
func configure(with setting: SettingType)
}
// Concrete Settings
final class Concrete1Setting: SettingType {
var color: UIColor = .blue
}
final class Concrete2Setting: SettingType {
var color: UIColor = .green
}
class CellTypeClass: UIView, CellType {
func configure(with setting: SettingType) {
fatalError("You must override this function")
}
}
// Concrete Cells
final class Concrete1Cell: CellTypeClass {
override func configure(with setting: SettingType) {
print("Configured Cell 1")
self.backgroundColor = setting.color
}
}
final class Concrete2Cell: CellTypeClass {
override func configure(with setting: SettingType) {
print("Configured Cell 2")
self.backgroundColor = setting.color
}
}
// Generic generator
func makeConcreteCell(_ setting: SettingType) -> CellTypeClass {
switch setting {
case is Concrete1Setting:
return Concrete1Cell()
case is Concrete2Setting:
return Concrete2Cell()
default: fatalError()
}
}
// Test
var cells: [CellTypeClass] = [Concrete1Cell(), Concrete2Cell()]
let settings: [SettingType] = [Concrete1Setting(), Concrete2Setting()]
for setting in settings {
cells.append(makeConcreteCell(setting))
}

Type ??? has no member

I am new to Swift and am trying to follow the Stanford Uni course CS193P.
I did originally type the code in but got multiple errors, so as I could not find a soluion on the forums, I copied the code in from the Democode provided.
Now I get errors, but not the same as before.
Here is the file that has the errors:
//
//ViewController.swift
// FaceIt
//
// Created by CS193p Instructor.
// Copyright © 2017 Stanford University. All rights reserved.
//
import UIKit
class ViewController: UIViewController
{
var expression = FacialExpression(eyes: .closed, mouth: .frown) {
didSet {
updateUI()
}
}
private func updateUI()
{
switch expression.eyes {
case .open:
FaceView?.eyesOpen = true
case .closed:
FaceView?.eyesOpen = false
case .squinting:
FaceView?.eyesOpen = false
}
FaceView?.mouthCurvature = mouthCurvatures[expression.mouth] ?? 0.0
}
private let mouthCurvatures =
[FacialExpression.Mouth.grin:0.5,.frown:-1.0,.smile:1.0,.neutral:0.0,.smirk:-0.5]
}
I can't understand why it says "Type 'FaceView?'has no member 'eyesOpen'
as in the file FaceView it contains the code:
class FaceView: UIView
{
// Public API
// 1.0 is full smile and -1.0 is full frown
#IBInspectable
var mouthCurvature: Double = 0.5 { didSet { setNeedsDisplay() } }
#IBInspectable
var eyesOpen: Bool = true { didSet { setNeedsDisplay() } }
#IBInspectable
var scale: CGFloat = 0.9 { didSet { setNeedsDisplay() } }
#IBInspectable
var lineWidth: CGFloat = 5.0 { didSet { setNeedsDisplay() } }
#IBInspectable
var color: UIColor = UIColor.blue { didSet { setNeedsDisplay() } }
func changeScale(byReactingTo pinchRecognizer: UIPinchGestureRecognizer)
{
I also get the same type of error for 'mouthCurvature'.
The only doubt I have is the type of file that I set up FacialExpression, as in the lecture the lecturer dragged it inform somewhere. I set it up as a Cocoa Touch file. Is this why I get the error?
Otherwise can anybody explain why I am getting this error?
Your eyesOpen property is an instance property—it exists on an instance of the FaceView type, not on the FaceView type itself. You should probably declare a property of type FaceView?, and then refer to that instead of the class name (the time to use the class name is when you are accessing a member that is declared class var/let/func or static var/let/func, and thus belongs to the type itself rather than to a specific instance of the type). So, do something like:
class ViewController: UIViewController {
var faceView: FaceView?
// add some code somewhere, maybe in viewDidLoad, to assign something to faceView
private func updateUI() {
switch self.expression.eyes {
case .open:
self.faceView?.eyesOpen = true
// ... etc ...
}
}

How to use one variable/property instead assigning same property three times?

Let's assume that myVar has same functionality in every implementation of view. I am trying to figure out how to declare/expose some kind of set-only property instead of assigning them n-times (with every new view created), but nothing comes to my head. How could I refactor into one line & one time assignment?
var myVar: (()-> Void)?
private func callBack() {
someClass.view1.myVar = self.myVar
someClass.view2.myVar = self.myVar
someClass.view3.myVar = self.myVar
}
// MARK: - someClass pseudocode
someClass: class {
let view1: CustomView: = CustomView
let view2: CustomView: = CustomView
let view3: CustomView: = CustomView
}
// MARK: - customView pseudocode
class CustomView: UIView {
var myVar: (()-> Void)?
}
something like this, but having all CustomViews in an array is good idea and could be implemented here as well
var a: (() -> Void)?
class CustomView: UIView {
var myVar: (() -> Void)?
}
class SomeClass {
let view1 = CustomView()
let view2 = CustomView()
let view3 = CustomView()
var myVar: (() -> Void)? {
set {
self.view2.myVar = newValue
self.view1.myVar = newValue
self.view3.myVar = newValue
}
get {
return self.myVar
}
}
}
let b = SomeClass()
b.myVar = ({print(3)})
b.view1.myVar!()
Is this what you are trying to do?
[someClass.view1, someClass.view2, someClass.view3].forEach { $0.myVar = self.myVar }
This is how I tend to deal with these issues:
class OtherClass {
var text: String?
init(text: String?) {
self.text = text;
}
}
class TestClass {
var thing1: OtherClass?
var thing2: OtherClass?
var thing3: OtherClass?
var allTheThings: [OtherClass?] { return [thing1, thing2, thing3]}
var ownText: String? {
didSet {
allTheThings.forEach { $0?.text = ownText }
}
}
}
Depending on how much you expect things to change you could make the array property a constant you set in your init rather than a computed property.
If you want to get fancy you could also do something like this for setting:
private var allTheThings: [OtherClass?] {
get {
return [thing1, thing2, thing3]
}
set {
guard newValue.count == 3 else {
//probably should put an assertion in here
return
}
thing1 = newValue[0]
thing2 = newValue[1]
thing3 = newValue[2]
}
}
init() {
self.allTheThings = Array(repeating: OtherClass(text: "Test"), count: 3)
}

Subclassing NSControl, IBAction is not called in Swift

I've subclassed an NSSlider that behaves differently whether the option key is pressed. In order to do that, I overrode the mouseDown func. It seems to do the job.
The thing is, I've noticed the connected #IBAction in my ViewController is only triggered when the option key is unpressed (i.e. when the mouseDown method is passed to super). What am I missing in order to allow the #IBAction to perform?
Many thanks
Besides the issue, improvement advices on the code are welcome... :-)
Josh
class ViewController: NSViewController {
#IBOutlet weak var theSlider: MySlider!
#IBAction func moveSlider(sender: NSSlider) {
print(sender.floatValue) //works only with optionKey unpressed
}
}
class MySlider: NSSlider { //Implemented in another source file
#IBInspectable var multiplier: Float = 0.5
private var modifierKeys = NSEventModifierFlags.AlternateKeyMask
private var optionKeyPressed = false
private var previousSliderPosition: Float = 0.0
//MARK: Init with NSCoder
required init?(coder: NSCoder) {
super.init(coder: coder)
Swift.print("init Coder called")
self.continuous = true
NSEvent.addLocalMonitorForEventsMatchingMask(.FlagsChangedMask) { (theEvent) -> NSEvent? in
self.flagsChanged(theEvent)
return theEvent
}
}
//MARK: Mouse tracking
override func mouseDown(theEvent: NSEvent) {
if optionKeyPressed {
var keepOn = true
previousSliderPosition = self.floatValue * Float(self.bounds.width) / Float(self.maxValue)
while keepOn {
if let nextEvent = self.window?.nextEventMatchingMask(Int(NSEventMask.LeftMouseUpMask.rawValue) | Int(NSEventMask.LeftMouseDraggedMask.rawValue))
{
switch nextEvent.type
{
case .LeftMouseDragged:
let mouseInnerLocationX = Float(self.convertPoint(nextEvent.locationInWindow, fromView: self.superview).x)
let mouseDelta = mouseInnerLocationX - previousSliderPosition
let newSliderPosition = previousSliderPosition + (mouseDelta) * multiplier
self.floatValue = newSliderPosition * Float(self.maxValue) / Float(self.bounds.width)
break
case .LeftMouseUp:
keepOn = false
break
default:
break
}
}
}
} else {
super.mouseDown(theEvent)
}
}
//MARK: Option key handling
override func flagsChanged(theEvent: NSEvent) {
if (theEvent.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == NSEventModifierFlags.AlternateKeyMask.rawValue {
optionKeyPressed = true
} else {
optionKeyPressed = false
}
}
}
If you're not calling super.mouseDown, you need to send the action yourself:
sendAction(action, to: target)
sendAction(_:to:), action and target are existing members of NSControl.