In Swift, how can you invoke a subclass's function in the base class's init method? Essentially, the goal is to ensure each subclass invokes its own version of initGrid, and then all the code to set numRows and numCols is defined only once, in the base class.
However, when initializing a subclass, the initGrid function from the base class -- not the subclass -- is run instead. This results in an array index exception since grid is empty unless the subclass creates it.
class BaseGrid {
var grid = [[NodeType]]()
var numRows = 0
var numCols = 0
init() {
// Init grid
initGrid()
// Set <numRows>
numRows = grid.count
// Set <numCols>
numCols = grid[0].count
// Verify each row contains same number of columns
for row in 0..<numRows {
assert(grid[row].count == numCols)
}
}
// Subclasses override this function
func initGrid() {}
}
class ChildGrid: BaseGrid {
override init() {
super.init()
}
override func initGrid() {
grid = [
[NodeType.Blue, NodeType.Blue, NodeType.Blue],
[NodeType.Red, NodeType.Red, NodeType.Red],
[NodeType.Empty, NodeType.Blue, NodeType.Empty]
]
}
}
if you will subclass and override initGrid, it will call method in current scope.
In base class, you can stay it empty, as abstract.
I.e.:
class AdvancedGrid: BaseGrid {
override init() {
super.init()
}
override func initGrid() {
// here is you custom alghoritm
}
}
Related
I am studying delegate pattern for swift on below code. I can not be pretty sure how can I use this option "without the need to reinstantiate the view.
protocol ShapeViewDelegate {
func drawShapeView(_ shapeView: ShapeView)
}
class ShapeView: UIView {
var strokeColor: UIColor?
var fillColor: UIColor?
var delegate: ShapeViewDelegate? {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
delegate?.drawShapeView(self) // self means object( ShapeView() ) is it instantiated ?
}
}
View object supposed to ready coming from delegate object but I didn't instantiate it, where this object instantiated using protocol automatically instantiated it at the run time. So I am writing an example like this :
class ShapeViewController: ShapeViewDelegate {
drawShapeView(view)
}
View is instantiated in other words occupied the memory at this example ?
You have to define drawShapeView() in ShapeViewController. In your code you are using or calling drawShapeView() which is already being called in your ShapeView. And about instantiate, yes you need to instantiate ShapeView in ShapeViewController or any other place you are confirming the delegate.
Code in your ShapeViewController -
class ShapeViewController: ShapeViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let shape = ShapeView(...)
shape.delegate = self
view.addSubView(shape)
}
func drawShapeView(_ shapeView: ShapeView) {
//your code here
}
}
Via this feature, you can have many definitions of ShapeView based on different ShapeViewController instances without worrying to modify ShapeView.
I am working an an app that involves a wheel. I want to execute a function in another class when the angular velocity reaches 0 after being spun.
The setup is that I have a SKView embedded in a view controller. I have a class called WheelView that is tied to that SKView.
Full class: https://github.com/swiftishard/wheelpractice2/blob/master/WheelView.swift
This is the relevant portion of code.
class WheelDelegate: NSObject, SKSceneDelegate {
func didSimulatePhysics(for scene: SKScene) {
guard let wheelScene = scene as? WheelScene else { return }
if (wheelScene.wheel.physicsBody?.angularVelocity ?? 0.0) > 0.0 {
print(wheelScene.wheel.physicsBody?.angularVelocity)
if(wheelScene.wheel.physicsBody?.angularVelocity ?? 0.0) < 25.0 {
wheelScene.wheel.physicsBody?.angularVelocity = 0.0
if(wheelScene.wheel.physicsBody?.angularVelocity ?? 0.0) == 0.0 {
print("CALL METHOD FROM ANOTHER VIEW CONTROLLER")
}
}
}
}
}
Main view controller where method to call is located
import UIKit
class Main: UIViewController {
func methodToCall() {
print("METHOD CALLED")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
So how would I call methodToCall from the didSimulatePhysics code?
I'm assuming WheelDelegate is created within Main as an object.
Multiple ways you can do this:
1) Most common way I have seen is to create a protocol as well as some delegates.
Inside Main class, right below all the imports, you can do.
protocol ViewControllerTalkingDelegate {
func methodToCall()
}
Inside WheelDelegate you add this as a global variable
var delegate:ViewControllerTalkingDelegate?
Then, whenever you create WheelDelegate inside Main
let wheelDelegate = WheelDelegate()
wheelDelegate.delegate = self
Then, inside Main at the bottom, you can do
extension Main:ViewControllerTalkingDelegate {
func methodToCall() {
//Do Something
}
}
Now, inside WheelDelegate you can do delegate.methodToCall().
2) Other way I have seen is to pass the 1st class as a variable to the 2nd class.
Inside WheelDelegate add a global variable
var myViewController:Main?
You can then either take in Main as a parameter when initializing or when you create WheelDelegate you can do
var wheelDelegate = WheelDelegate()
wheelDelegate.myViewController = self
Then inside WheelDelegate you can do
self.myViewController.methodToCall()
This question already has answers here:
Calling instance method during initialization in Swift
(8 answers)
Closed 6 years ago.
I have a class
class ChartView: UIView
{
class: DotView {
let circleView1: UIView
let circleView2: UIView
init (view: UIView)
{
self.view = view
self.circleView1 = self.buildCircle(some rect here)
self.circleView2 = self.buildCircle(some rect here)
func buildCircle(rect: CGRect) -> UIView
{
let dotView = UIView(frame: rect)
dotView.backgroundColor = UIColor.whiteColor()
dotView.layer.cornerRadius = dotView.bounds.width / 2
self.view.addSubview(dotView)
return dotView
}
}
}
But I got this error:
Use of 'self' in method call 'buildCircle' before all stored properties are initialized
So I just want to create objects in some method and then assign it to the stored properties. How can I fix my code?
You can't call methods on self before all non-optional instance variables are initialized.
There are several ways to go around that.
Change properties to optionals or implicitly unwrapped optionals
(not recommended)
Make the buildCircle() method static or just a
function in the file and call the addSubview() for all the circles
after all of the properties were initialized and you called
super.init()
etc. You just have to avoid calls to self before the
class was initialized.
For solving of this issue it is possible to use these approaches:
class MyClass: NSObject {
var prop: String = ""
override init() {
super.init()
self.setupMyProperty()
}
func setupMyProperty() {
prop = "testValue"
}
}
class MyClass1: NSObject {
var prop: String = ""
override init() {
prop = MyClass1.setupMyProperty()
super.init()
}
class func setupMyProperty() -> String{
return "testValue"
}
}
You can create your circleView in the function willMoveToSuperview.
In this lifecycle function you are sure to have ChartView allocated properly, so:
override func willMoveToSuperview(newSuperview: UIView?) {
self.circleView1 = self.buildCircle(some rect here)
self.circleView2 = self.buildCircle(some rect here)
}
How do you define a read-only property in Swift? I have one parent class which needs to define a public property eg. itemCount. Here's my code:
Class Parent: UIView {
private(set) var itemCount: Int = 0
}
class Child {
private(set) override var itemCount {
get {
return items.count
}
}
}
I get the error: Cannot override mutable property with read-only property
Option 1 - Protocols:
Well I can't use a protocol because they can't inherit from classes (UIView)
Option 2 - Composition:
I add a var view = UIView to my Child class and drop the UIView inheritance from my Parent class. This seems to be the only possible way, but in my actual project it seems like the wrong thing to do, eg. addSubview(myCustomView.view)
Option 3 - Subclass UIView on the Child class
I can't do this either because I intend to have multiple related Child classes with different properties and behaviour, and I need to be able to declare instances of my Child classes as the Parent class to take advantage of UIView's properties and Parent's public properties.
You can use a Computed Property which (like a method) can be overridden.
class Parent: UIView {
var itemCount: Int { return 0 }
}
class Child: Parent {
override var itemCount: Int { return 1 }
}
Update (as reply to the comment below)
This is how you declared and override a function
class Parent: UIView {
func doSomething() { print("Hello") }
}
class Child: Parent {
override func doSomething() { print("Hello world!") }
}
You can declare setter as private while getter is public.
public class someClass {
public private(set) var count: String
}
Refer to this link
As one more option you can use private variable for read/write and another for read-only. Count you're using for internal class changes, and numberOfItems for public access. Little bit weird, but it solves the problem.
class someClass {
private var count: Int = 0
var numberOfItems: Int { return count }
func doSomething() {
count += 1
}
}
I have a random integer that needs to update every second, but my issue is that the random int cannot be defined outside of a function because it is an random int with the range of 0 to the screen size
var randomX = UInt32(self.size.width)
I cannot use self.size.width outside of a function. At first I thought maybe its not updating because its 1. Declared in the function didMoveToView() and 2. I declared it using "let" instead of "var". If I declare it in my function that updates every second, the variable cannot be used outside of that function which is a huge problem.
You can declare your var as an instance variable of your class (outside a method), update it in a method, and use it anywhere else in an instance of that class.
class RTest {
var randomX: UInt32
init() {
self.randomX = 2;
}
func rx5()->UInt32 {
return self.randomX * 5
}
}
Create a global class variable so you could access it anywhere in the class
class MyClass: UIViewController {
var screenWidth: CGFloat = 0.0; //Global variable for this class
func getRand() -> UInt32 {
//generate your random number
return UInt32(self.screenWidth)
}
func useRandom() {
//call getRand anytime you want to generatea a new random num
var myRandNum = getRand()
}
func override viewWillAppear(animated: Bool) {
self.screenWidth = self.view.frame.size.width
}
}
Code not tested but should give you an idea on how to go about it.
hope that helps.