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 ...
}
}
Related
I am trying to provide a default value for a variable in a Protocol. I am getting an error:
Type ViewController does not conform to protocol Test
Code:
protocol Test {
var aValue: CGFloat { get set }
}
extension Test {
var aValue: CGFloat {
return 0.3
}
}
class ViewController: UIViewController, Test {
override func viewDidLoad() {
super.viewDidLoad()
print("value \(aValue)")
}
}
How can I provide a default value so the ViewController can use the default value (in the protocol extension) without to declaring it?
protocol Test {
var aValue: CGFloat { get set }
}
extension Test {
var aValue: CGFloat {
get {
return 0.3
}
set {
print("the new value is \(newValue)")
}
}
}
class Default: Test {
init() {
print("value \(aValue)")
}
}
class ViewController: Test {
var aValue: CGFloat {
get {
return 0.4
}
set {
print("i am overriding the setter")
}
}
init() {
print("value \(aValue)")
}
}
var d = Default() // value 0.3
d.aValue = 1 // the new value is 1.0
var vc = ViewController() // value 0.4
vc.aValue = 1 // i am overriding the setter
Since you have a protocol extension, you don't have to implement neither the getter nor the setter if you don't want to.
https://docs.swift.org/swift-book/LanguageGuide/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259
In addition to stored properties, classes, structures, and
enumerations can define computed properties, which do not actually
store a value. Instead, they provide a getter and an optional setter
to retrieve and set other properties and values indirectly.
You can't set the value of the same variable in the setter itself.
protocol Test {
var aValue: CGFloat {get set}
}
extension Test {
// var aValue: CGFloat {
// return 0.3
// }
var aValue: CGFloat {
get {
return 0.3
}
set {
debugPrint("new value is \(aValue)")
}
}
}
struct TestClass: Test {
func printData() {
debugPrint(aValue)
}
}
let aTestClass = TestClass()
aTestClass.printData()
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
}
This is probably 2 swift questions in one...
How do I solve a situation where I want to extend an existing base class (UIView in my case) with functionality that requires stored properties? ...so that I can reuse the code for other classes?
I have tried to solve it through composition below, but I don't know if there is a more obvious way that I just can't see as I am fairly new to swift...
The second question:
In my implementation I have an abstract class ManagedComponentImpl which needs an eventReceiver object which is going to be the containing UIView subclass.
The problem I have with my implementation is that swift forces me to define an object binding where Receiver:NSObject for ManagedComponentImpl, so that I can declare the optional variable eventReceiver as weak. (and I guess I would create a memory leak otherwise). However I would want to use this implementation on a variety of objects (which could of course all inherit NSObject, but they do not actually need to for other reasons but this, so it seems odd). So question number 2: Is there a way to avoid this?
EDIT: And yes! I made a mistake mixing model and view code here, but I guess the fundamental problem remains when you switch UIViewController for UIView :-)
public protocol ManagedConnection {
var connectionKey:String { get set }
}
public protocol ManagedComponent: ConnectionObserver {
var connectionKey:String { get set }
func connectTo()
func disconnectFrom()
}
public protocol EventReceiver: ConnectionObserver {
var variableSet:Set<VariableID>? { get }
var handleVariableUpdates: ((Set<VariableID>)->Void)? { get }
}
class ManagedComponentImpl<Receiver: EventReceiver> where Receiver:NSObject {
public var _connectionKey: String = Shared
//The connection Key
public var connectionKey: String
{
set {
disconnectFrom()
self._connectionKey = newValue
connectTo()
}
get {
return _connectionKey
}
}
// The varset needed by this control
weak var eventReceiver:Receiver!
// handler for the status pane variables
//
var connectionObserverHandlerID:UInt16 = 0
var eventHandlerID:UInt16 = 0
public init(receiver:Receiver) {
self.eventReceiver = receiver
}
public func connectTo() {
guard let manager = Connections.shared[self.connectionKey] else { return }
let connection = manager.connection
// disconnect any previous connections
disconnectFrom()
// Connect the connection observer
connectionObserverHandlerID = connection.addConnectionObserver(observer: eventReceiver)
if let variableSet = eventReceiver.variableSet, let handler = eventReceiver.handleVariableUpdates {
eventHandlerID = connection.requestVariables(variables: variableSet, handler: handler)
}
}
public func disconnectFrom(){
guard let manager = Connections.shared[self.connectionKey] else { return }
let connection = manager.connection
// Disconnect
if connectionObserverHandlerID != 0 {
connection.removeConnectionObserver(id: connectionObserverHandlerID)
}
if eventHandlerID != 0 {
connection.unRequestVariables(ident: eventHandlerID)
}
}
}
class ManagedUIView: UIView, ManagedComponent, EventReceiver {
private var component:ManagedComponentImpl<ManagedUIView>!
public var variableSet:Set<VariableID>?
public var handleVariableUpdates:((Set<VariableID>)->Void)?
public override init(frame: CGRect) {
super.init(frame: frame)
component = ManagedComponentImpl<ManagedUIView>(receiver: self)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
component = ManagedComponentImpl<ManagedUIView>(receiver: self)
}
var connectionKey:String {
set {
component.connectionKey = newValue
}
get {
return component.connectionKey
}
}
func connectTo() {
component.connectTo()
}
func disconnectFrom() {
component.disconnectFrom()
}
func notifyState(state: ConnectionState) {}
}
Okay - for everybody reading this, the answers are:
- The problem should probably be solved by a delegate and not by inheritance.
- To avoid inheriting from NSObject: the problem seems to be that protocols can not only be implemented by classes. Therefore the protocol needs a class limitation to work as weak references. As a result ManagedComponentImpl does not need to be generic any more and I can just have a weak CAPEvent receiver optional.
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.
I can not figure out why Xcode playground is crashing, here is my basic setup
protocol Shootable {
func shoot()
}
class BaseMissile: Shootable {
var damage = 0
func shoot() {
println("Pew pew for \(damage) damage")
}
}
class Missile1: BaseMissile {
override init() {
super.init()
damage = 1
}
}
protocol Targetable {
var life: Int {get set}
}
class BaseSpaceship: Targetable {
var life = 0
var currentMissile: BaseMissile!
func printHealth() {
println("My current life: \(life)")
}
}
class Spaceship1: BaseSpaceship {
override init() {
super.init()
life = 1
currentMissile = Missile1()
}
}
var spaceship = Spaceship1()
spaceship.printHealth()
spaceship.currentMissile.shoot()
specifically the last line spaceship.currentMissile.shot() crashes the playground. If I move var currentMissile: BaseMissile! from BaseSpaceship to Spaceship1 it works, but is that an acceptable way to handle this?
I guess this happens because BaseSpaceship class has no initializers and var currentMissile: BaseMissile! cant be nil. So you either should do it optional by replacing ! with ? or give it a default value.