I want to load from a UIViewController a SwiftUI based view that reads a json local to the bundle. The code and binding in a swiftUI only project works fine, when I leverage the UIHostingController I get an error for which I am seeking help to fix.
class ProgramsListVCHost : UIHostingController<ProgramsList>, Storyboarded {
weak var coordinator: MainCoordinator?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder, rootView: ProgramsList())
}
}
The code above crashes because I didn't pass what I should have to cause the init of the model.
super.init(coder: aDecoder, rootView: ProgramsList().environmentObject(ProgramUserData()))
However, if I do that the compiler demands that I cast the whole thing to the underlying class ProgramList (Cannot convert value of type 'some View' to expected argument type 'ProgramsList')
If I do apply the recommended fix, the code crashes with the error:
Could not cast value of type
'SwiftUI.ModifiedContent>>'
(0x7fff89d0ec68) to 'bikeit.ProgramsList' (0x110477328). 2019-11-15
14:36:26.049041-0500 bikeit[13658:8386085] Could not cast value of
type 'SwiftUI.ModifiedContent>>'
(0x7fff89d0ec68) to 'bikeit.ProgramsList' (0x110477328). (lldb)
I don't understand how to initialize the object that I must pass in order to make it work.
Of course, it is because your declared content UIHostingController<ProgramsList> is ProgramsList, so it is expected in initialiser, but you pass to it output of .environmentObject, which is SwiftUI.ModifiedContent, so types checking fails and you get compiler error.
It is not clear your needs, but possible solution could be as follows:
class ProgramsListVCHost : UIHostingController<AnyView>, Storyboarded {
weak var coordinator: MainCoordinator?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder, rootView: AnyView(ProgramsList().environmentObject(ProgramUserData())))
}
}
Update: found alternate
class ProgramsListVCHost : UIHostingController<ProgramsListVCHost.MyView>, Storyboarded {
// Internal view
struct MyView: View {
var body: some View {
ProgramsList().environmentObject(ProgramUserData())
}
}
weak var coordinator: MainCoordinator?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder, rootView: MyView())
}
}
Related
I have two classes in swift. I have an action method that I want to override in many subclasses that will do a different action. I wanted this to sort of function like an abstract class in Java, where I could pass a bunch of different subclasses of a certain class, but they would be referred to from the superclass so that one method could be called for all of them even though they are different subclasses with different overridden versions of that one method. Here is what I mean.
class1: SKSpriteNode{
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func action(){ print("this should never be called") }
}
class2: class1 {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func action() {
self.physicsBody?.affectedByGravity = true
}
}
I would want to do something like this. A node with name someName would be defined as a class2 in the SKScene editor.
class GameScene...
let node = self.childNode(withName: "someName") as? class1
noe.action()
However, I never get the output I want. I always get the call of class1 even when I want it to be overridden as a class2 and turn on gravity for that node. I would want to call the action method for many subclasses of class1 no matter the subclass, so just changing the class1 method to do what I want will not work.
Thank you
EDIT: Here are my actual classes:
So I have some classes that are nodes that will be triggered when the user passes the trigger node. It has a stored value for the name of the target node that is then retrieved from the main SKScene, its parent. Then I want to have a specific TargetNode, DropNode that will make it fall from the sky, but I want to be able to trigger any subclass of TargetNode by calling the TriggerAction method, but it never seems to perform the code of the subclass, the DropNode, but instead always does nothing/ whatever code I put in the action method of the TargetNode class. It never overrides the method.
//SUPERCLASS:
import Foundation
class SKTargetNode: SKGlowNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func action(){
print("i don't want this to be called")
}
}
//SUBCLASS:
import Foundation
class SKDropNode: SKTargetNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func action() {
self.physicsBody?.affectedByGravity = true
}
}
//Just a class that triggers any TargetNode
import Foundation
import SpriteKit
class SKTriggerNode: SKSpriteNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func triggerAction(_ scene: GameScene){
let nodeName = self.userData?.value(forKey: "TargetNode") as? String ?? ""
let node = scene.childNode(withName: "//\(nodeName)") as? SKTargetNode
node?.action()
}
}
I created a UIView subclass:
class RA_Circle: UIView {
let elipseWidth:CGFloat
let elipseHeight:CGFloat
init(elipseWidth: CGFloat, elipseHeight:CGFloat) {
self.elipseWidth = elipseWidth
self.elipseHeight = elipseHeight
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
But the compiler is giving me this error on the required init:
Property 'self.elipseWidth' not initialized at super.init call
I was following this SO Q&A:
How to properly init passed property in subclass of UIView?
In your class definition you're declaring your variable without any default values, and the required initializer is not providing any values before calling super init.
There are a number of ways to deal with this, here's a few...
One way is to provide default values in the property declarations:
var elipseWidth: CGFloat = 0.0
var elipseHeight: CGFloat = 0.0
The second is to make your properties optional:
var elipseWidth: CGFloat?
var elipseHeight: CGFloat?
A third is to provide default values in the required initializer:
required init?(coder aDecoder: NSCoder) {
elipseWidth = 14.0
elipseHeight = 14.0
super.init(coder: aDecoder)
}
You have to change the let variable declarations to var and optional. In this case, you would initialize them in awakeFromNib() or at some later time.
Seems like a trivial issue but I am not able to make this compile.
Neither in playgrounds nor in normal swift ios projects.
(Please note I am not using Storyboards that's why I don't need / care about the init?(coder) part..it;s jsut it has to be include otherwise the complier complains about it.)
class SegmentedControl: UISegmentedControl {
let configuration: [String]
required init(configuration: [String]) {
self.configuration = configuration
super.init(items: configuration)
}
required init?(coder aDecoder: NSCoder) { fatalError() }
}
let x = SegmentedControl(configuration: ["a","b"])
It is complaining about not having the deisignated initializer implemented.
Fatal error: Use of unimplemented initializer 'init(frame:)' for class
'__lldb_expr_167.SegmentedControl'
I don't understand what is going on here. Isn't the designated initializer the init(items:) for UISegmentedControl? I am calling it in my subclass designated initializer.
Solution:
class SegmentedControl: UISegmentedControl {
var configuration = [String]()
required init(configuration: [String]) {
super.init(items: configuration)
self.configuration = configuration
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) { fatalError() }
}
let x = SegmentedControl(configuration: ["a","b"])
I face a problem when trying to instantiate a class(SettingsView) that uses NSCoder, in another class(Swipe). The SettingsView class is already an instance of an objective -C class(FXBlurView)
I get the following error "Missing argument for parameter 'coder' in call". I realized that I have to call some parameters, but I do not know which.
SettingsView :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.tintColor = UIColor.clearColor()
}
Swipe :
var settings = SettingsView()//error occurs here
i already tried to use
init () {
super.init (tint: ...)
}
But it seems to be not functionnal.
Ok , i succeed to do it just by adding :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
var settings = SettingsView(coder: aDecoder)
}
into my "Swipe" view controller.
I have a subclass of UIButton as follows:
class VectorizedButton: UIButton {
override var highlighted: Bool {
didSet {
setNeedsDisplay()
}
}
}
Everything works great, until I added this line in my root controller:
var twitterButton: TwitterButton?
TwitterButton extends VectorizedButton.
Here is the error I get:
...UIView+Vectorized.swift:42:7: Class 'VectorizedButton' has no initializers
...UIView+Vectorized.swift:44:18: Stored property 'highlighted' without initial value prevents synthesized initializers
Easy, let's set a default value:
override var highlighted: Bool = false
Tougher error:
<unknown>:0: error: super.init called multiple times in initializer
<unknown>:0: error: super.init called multiple times in initializer
<unknown>:0: error: super.init called multiple times in initializer
Try overriding init?
required init(coder aDecoder: NSCoder) {
highlighted = false
super.init(coder: aDecoder)
}
Even more errors:
error: 'self' used before super.init call
highlighted = false
^
error: 'self' used before super.init call
highlighted = false
^
Anyone cares to explain what's going on here?
You just had to add the required init
Here is what I did :
class VectorizedButton: UIButton {
override var highlighted: Bool {
didSet {
setNeedsDisplay()
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class TwitterButton: VectorizedButton {
}
And in a ViewController :
var twitterButton: TwitterButton?
Works fine like this.
You wanted to override the highlighted property and create the twitterButton as Optional, so you need to implement an init in your class.
You cannot set the highlighted property with this :
override var highlighted: Bool = false
You need to set it in the init, or like this :
override var highlighted: Bool {
get { return false }
set { super.highlighted = newValue }
}
And for this :
required init(coder aDecoder: NSCoder) {
highlighted = false
super.init(coder: aDecoder)
}
You need to set your property after the call to super.init.