How to specify nested custom view class? - ios

Available
nested classes SuperView and NestedView.
class SuperView : UIImageView {
class NestedView : UIImageView {
var text : String = "Nested View"
}
var text : String = "Super View"
var nested : NestedView?
}
I would like to set for a UIImageView the property named "Custom Class Name" to value "NestedView" inside the inspector of the storyboard scene. But the Interface Builder couldn't find "NestedView" class.

At this time, I think that Interface Builder only recognizes the names of Objective-C classes. You can still make Interface Builder find a nested class with the #objc keyword:
class SuperView: UIView {
#objc(SVNestedView) class NestedView: UIImageView {
}
}
Then, in Interface Builder, specify that th view is of class SVNestedView. Since Objective-C isn't namespaced, you still need to pick unique names for each nested class, but at least the Swift side is properly namespaced.

In Swift, an instance of an inner class is independent of any instance of the outer class. It is as if all inner classes in Swift are declared using Java's static.
I don't think this class design suits your requirement. What you need to do is, you need to create a new class outside your view, and create some sort of a composition. This will surely work out for you.
Here is some modification to your code:
class SuperView : UIImageView {
var text : String = "Super View"
var nested : NestedView = NestedView()
}
class NestedView : UIImageView {
var text : String = "Nested View"
}

I think it impossible to specify an inner class in a storyboard like SuperView.NestedView so far. So I makes a class extended from an inner class, then specifies it in a storyboard like SuperView_NestedView. It works for me.
final class SuperView_NestedView: SuperView.NestedView {} // Specifies this class in the storyboard
class SuperView : UIImageView {
class NestedView : UIImageView {
var text : String = "Nested View"
}
var text : String = "Super View"
var nested : NestedView?
}
This view can be referred as SuperView.NestedView from a ViewController because SuperView_NestedView is extended from SuperView.NestedView.
class TheViewController: UIViewController {
#IBOutlet var nestedView: SuperView.NestedView!

Related

iOS: MVVM-C ViewController super class

I have MVVM-C arch. Each UIViewController has a ViewModel and CoordinatorDelegate to notify the Coordinator when navigation needs to be performed. The code that create the VC repeat itself, and I though it would be great to create a super class to unify all static funcs that create the VC. Like this:
import UIKit
class MVVMCViewController: UIViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
static func initVC(storyboard: Storyboard,
coordinatorDelegate: CoordinatorDelegate?,
viewModel: Modelling?) -> Self {
let viewController = Self.instantiate(in: storyboard)
viewController.coordinatorDelegate = coordinatorDelegate
viewController.viewModel = viewModel
return viewController
}
}
All CoordinatorDelegateProtocols will inherit from CoordinatorDelegate and all ViewModels will be inheriting from Modelling
But the subclassing does not work smoothly.
Any ideas?
Hi this model wouldn't work fine.
MVVMCViewController has hardcoded protocols as variable type, so You should have the same in your childVC.
To make it work as u want MVVMCViewController show be generic (but can be a lot of issues with it), like
class MVVMCViewController<T: Modelling, U: CoordinatorDelegate>: UIViewController {
weak var coordinatorDelegate: U?
var viewModel: T?
}
or add just casted properties to ConnectViewController
class ConnectViewController: MVVMCViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
var currentDelegate: ConnectViewControllerCoordinatorDelegate? {
coordinatorDelegate as? ConnectViewControllerCoordinatorDelegate
}
var currentVM: ConnectViewModel? {
viewModel as? ConnectViewModel
}
}
Your superclass MVVMCViewController defines two properties coordinatorDelegate and viewModel. If you just need to access them in your child class ConnectViewController, just access it normally. You don't need to define it again.
Also, in your parent class, you have weak var coordinatorDelegate: CoordinatorDelegate?. But in your child class (ConnectViewController), you redeclare the property with a different type (ConnectViewControllerCoordinatorDelegate?). That is illegal, even if it is a subclass of CoordinatorDelegate.
Hence, either
Rename the property in child class to avoid the conflict
Keep the name and the type, but add an override keyword for the property if you plan to add additional functionality in your child class
Do not declare the property again at all in your child class if you don't need to add additional functionality to it. Just access it directly.
Refer to how inheritance works in Swift over here: https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html

Why does Interface Builder not show a view controller subclass in the identity inspector?

I have a BaseViewController and I want to inherit from this in a subclass. When I do this the subclass "TestViewController" doesn't appear under the class dropdown in Xcode's interface builder (see below image). Isn't this possible?
class BaseViewController<T: AnyObject> : UIViewController{
var test: T?
override func viewDidLoad() { }
}
class TestViewController : BaseViewController<UIView>{ }
If I just type in the class name then I get a runtime error saying:
Unknown class TestViewController in Interface Builder file
Tried
Removing references to files and adding them again
Editing the storyboard using a text editor and adding name to attribute "customClass"
Pretty simple: You can't use a generic type here. The reason is that UIKit is written in ObjC, and generic types are not fully visible to ObjC. This answer goes into the details.

Declaring a Property with Type Constraint in Swift

I need to create a custom fields framework in my app. I defined a protocol for the fields called FieldType and extended with it UITextField and UIButton to be different types of fields.
Now I want to create a container view for the fields so I want the container to be able to refer to its field elements as both UIViews and FieldTypes, and I'm wondering if there's a concise way to define the type of elements it receives to be a specific UIView that implements the FieldType protocol?
I can have FieldContainerView accept UIViews or FieldTypes and check manually that it also matches the other with a guard statement, but it feels a bit cumbersome.
I tried 2 approaches:
1) Define a Custom Intermediary FieldViewType
The idea is to have FieldViewType extend UIView directly with FieldType so it might be useful as a general case for UITextField: FieldType and UIButton: FieldType. But as this code sample clearly shows, this does not work.
protocol FieldType {
var showError: Bool { get set }
var isEmpty: Bool { get set }
}
class CustomTextField: UITextField, FieldType {}
class CustomButtonField: UIButton, FieldType {}
let textField = CustomTextField()
textField is UIView // True
textField is FieldType // True
let buttonField = CustomButtonField()
buttonField is UIView // True
buttonField is FieldType // True
class FieldView: UIView, FieldProtocol {}
let field = FieldView()
field is UIView // True
field is FieldProtocol // True
textField is FieldView // False
buttonField is FieldView // False
2) Use Generics
I can define a generic type that matches the requirements like so <FieldViewType: UIView where FieldViewType: FieldType>, but I don't see where to use to best solve my problem. If I define it at the class level
class FieldContainerView<FieldViewType: UIView where FieldViewType: FieldType>: UIView {
var fields = [FieldViewType]()
func addField(FieldViewType: field) {
fields.append(field)
}
}
I need to declare the container class once for each field type I'll want to use and won't be able to use 2 field types in the same container.
The other option is to define type constraint at the function level with addField
class FieldContainerView: UIView {
var fields = [UIView]()
func addField<FieldViewType: UIView where FieldViewType: FieldType>(FieldViewType: field) {
fields.append(field)
}
}
and then cast each element in fields to FieldType when necessary and I'll know the cast will always work because addField is the only way to add elements to the container. But this also feels too cumbersome.
It feels like the best way around this would have been to be able to define FieldViewType with a typealias, but this doesn't seem to be supported. Or have UIView be defined with a protocol so it could be mixed better, but UIKit isn't constructed in this manner.
So it seems that at the moment there's no way to create a type constraint in property declarations. I don't know why, but I don't know anything about language implementations.
I went for a workaround where FieldType also has a view: UIView property with a default implementation.
The new FieldType declaration:
protocol FieldType {
var showError: Bool { get set }
var isEmpty: Bool { get set }
var view: UIView { get }
}
extension FieldType where Self: UIView {
var view: UIView {
return self
}
}
This way it doesn't matter from which class in the UIKit hierarchy you inherited before conforming to the FieldType protocol, as long as you have UIView somewhere as your super class, you'll have an accessibly view property.
This feels like a workaround, but at least it saves dual-declarations for collections that need both the FieldType and the UIView properties of an object.
Can you change this line:
class FieldView: UIView, FieldProtocol {}
To this:
class FieldView: UIView, FieldType {}

In swift, how to refer to topmost class when my class has subclass of same name

In Swift:
How can I assign a topmost myObject to the innerObj variable?
Does swift have some sort of namespace operator that lets me create an myObject from global namespace?
Consider the code below.
//my object that can init with a message=string
class MyObject {
init(message: String) {
println(message)
}
}
//here I define a global works fine
let global = myObject(message: "this works")
//other class
class ViewController: UIViewController {
//defines an inner class with same name
class MyObject {
func failsFunction(){
//cannot invoke initializer for type "ViewController.myObject" with an argument of type (String)
let innerObj = myObject("how can I refer to the topmost myObject here?")
}
}
}
My first answer would be "don't do that." It's technically legal because the two classes have unique scope, but it's confusing as all hell, and will come back to bite you 6 months from now when you are coming back to this code and don't remember that you have a global class and a child class of ViewController with the same name.
If you are going to ignore that advice, Lou provided your solution: Create a typeAlias at the top level and use that inside your ViewController class so that you can reference the global class inside ViewController.
Secondly, class names should start with an upper-case letter. So class myObject should be class MyObject. This is a documented convention of the language.
Thirdly, myObject is a dreadful name for a class. It doesn't give you any idea what the class is for. Even if this is a learning exercise, you should still follow good coding practices. It trains good habits, and test code has a way of finding itself in real projects, or posted as demo code somewhere, or whatever.
You need to alias it before you hide it with:
typealias GlobalMyObject = MyObject
One usual way is to bind your outer class into struct. This pattern is quite similar to creating a namespace. You could do it like this
struct MyNameSpace {
class myObject {
init(message: String) {
print(message)
}
}
}
//here I define a global works fine
let global = MyNameSpace.myObject(message: "this works")
//other class
class ViewController: UIViewController {
//defines a subclass with same name
class myObject {
func failsFunction(){
//cannot invoke initializer for type "ViewController.myObject" with an argument of type (String)
let innerObj = MyNameSpace.myObject(message: "how can I refer to the topmost myObject here?")
}
}
}
Then, you could use both the classes and the compiler determines the use cases differently for both.

How to access a property of one class from another class?

I have one class:
class FoodListTable: UITableViewController
At the end of this class, I have a variable of the sum of numbers in an array:
var calorieTotal: Int {
return calorieNumberArray.reduce(0) { $0 + $1 }
}
I also have another class:
class Menu: Calculator { // Calculator is a class I had created earlier
In this class, I want to have a property that is the var calorieTotal: Int from class FoodListTable: UITableViewController. I have started this property:
let dividend =
How can I access the 'calorieTotal' property from the first class? I am very new to programming so please provide all the code possible. If you were wondering, I have looked for this question, but I still can't seem to figure it out?

Resources