I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint
Related
I have a base class type and a subclass type, where the subclass includes a generic type. If I have the subclass stored in the form of the base class type but I would like to type cast it back to the subclass type, Swift won't seem to let me.
Below is an example of what I mean:
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
let a: [Base] = [Next(view: UIImageView()), Next(view: UILabel())]
for item in a {
if let _ = item as? Next {
print("Hey!")
}
}
Why is "Hey!" never printed?
EDIT:
"Hey!" is printed if the cast reads:
if let _ item as? Next<UIImageView> ...
but only for the case of the UIImageView class.
and
"Hey!" is printed if one of the items in the array a is:
Next(view: UIView())
Ideally, I would like to not know what type the generic is when casting, but I realise this may not be possible.
The generic Next<T> is a template of sorts that creates unique separate classes. Next<UIView> and Next<UIImageView> are two completely unrelated types.
You can unite them with a protocol:
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
protocol NextProtocol { }
extension Next: NextProtocol { }
let a: [Base] = [Next(view: UIImageView()), Next(view: UILabel()), Base()]
for item in a {
if item is NextProtocol {
print("Hey!")
} else {
print("Only a Base")
}
}
Output:
Hey!
Hey!
Only a Base
By defining a protocol such as NextProcotol that all classes derived from Next<T> conform to, you can refer to them as a group and distinguish them from other classes that derive from Base.
Note: To check if an item is of a type, use is instead of checking if the conditional cast as? works.
This is where protocols comes to help.
you need a dummy protocol to confirm Next to it
And use that dummy protocol to check your items types.
Therefore we create a dummy protocol confirm Next to it and use that protocol to compare items.
code would be something like this.
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
protocol MyType {
}
extension Next: MyType {}
let a: [Base] = [Next(view: UILabel()), Next(view: UILabel())]
for item in a {
if let _ = item as? MyType {
print("Hey!")
}
}
UIView is super class for all the UIElements.
saying this will result into true,
if let _ = UILabel() as? UIView {print("yes") }
Next does not confirm to UIView but rather it requires something that confirm to UIView therefore you can't use the subs of UIView to check if they are Next, next have no exact type,
here we use the dummy protocol above !
When I saw your example I was expecting it to fail during compilation.
It seems that as long as you specify a concrete base class for your T (in this case UIView), then the compiler can infer that class in as or is statements. So when you type item as? Next the compiler understands item as?Next` because UIView is a concrete type.
This would not work if instead of UIView you would use a protocol. Also, simply using Next instead of Next<UIView> (or Next<concrete subclass of UIView>) will result in a compiler error outside an is or as statement.
I have 2 classes in Swift in different Swift files, and I'm trying to call a method from the other class, and pass an integer as an argument. I have the following:
Class 1:
Inschatting.wentBack(Punten)
Class 2:
class Inschatting : UIViewController {
var Punten:Int = 0;
#IBOutlet var inschattingAdvies: UILabel!
func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
}
Given error: Cannot convert value of type "Int" to expected argument type 'Inschatting'
Bonus question: Class 2 also complaints about the fact I want to put down a String, but it's clearly an Int
When you want to call your wentBack()-Func like above you should declare it as a class function... otherwise you should create an instance of Inschatting.
the problem is that Inschatting should declare the function the following way:
static func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
But the problem is that, that you have an instance value the wentBack function.
self.inschattingAdvies
Now what you need to do is to make a decision:
1. you should call the wentback function on a Inschatting instance.
In Class A:
let instance = Inschatting()
instance.wentBack(5)
2. You should remove the self.inschattingAdvies from wentBack.
Answer to your bonus question:
"Punten" does not seem to be an integer, or an enum value at all to me.
Since your Inschatting class is a UIViewController, I suspect you want to display the data from your 1st class in your UIViewController. If this is indeed true your mistake is that you are using a Class instead of an instance of the class.
If you indeed want to display the data from your 1st class in your 2nd class (The VC), you will need to create a class instance of your second class.
let inschattingVC = Inschatting()
this way you can use inschattingVC.wentBack(someRandInt) to call your function and set the text accordingly.
Secondly your Punten in Inschatting.wentBack(Punten) is also probably not a variable but again an instance (or you should rename it. Variables should start with lowercase letters!!!).
So if you are creating a class instance using Inschatting() you should be able to use .wentBack(12345) to set the text in your class instance.
First you say :
#IBOutlet var inschattingAdvies: UILabel!
so inschattingAdvies is a UILabel
Then you have a function :
func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
where you have Punten which is an integer. And then you try to set the text property of your label to punten.
Your code should be :
func wentBack(Punten:Int) {
self.inschattingAdvies.text = String(Punten)
}
note the cast to a string.
Not sure if that's the error you were getting, but it's definitely also an error.
I need to "read" ViewController, which was sent as an argument to a function, as a VC of the specific class. So I need something like that (we get a class also from a function arguments):
let vc = vc_from_func_args as! type_from_func_args
I can pass a class to let's say isMemberOfClass() by doing that:
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
But I can't do the same thing with "as" expression. It gives me an error:
klass is not a type
How can we pass class (type?) after "as" as a variable?
Given your comments, this is exactly what protocols are for. If you want a thing you can call pop on, then make that a requirement for your function. If it's easy to list all the things you need, then just put them in your protocol:
protocol Stackable {
var parent: UIViewController {get set}
var child: UIViewController {get set}
}
func push(vc: Stackable) {
// do the work
}
If you really need this to be a UIViewController that also happens to be Stackable, that's fine, too:
func pop<VC: UIViewController where VC: Stackable>(vc: VC) {
// do the work
}
Then just mark your view controllers as conforming to Stackable:
class SomeViewController: UIViewController, Stackable {
var parent: UIViewController
var child: UIViewController
...
}
If you find yourself doing a lot of as! or AnyClass, you're probably on the wrong track in Swift.
How about something like that...
if let checkedClass: MyFirstClass = vc_from_func_args as? MyFirstClass {
//It only hits here if it is MyFirstClass
}
if let checkedClass: MySecondClass = vc_from_func_args as? MySecondClass {
//It only hits here if it is MySecondClass
}
if let checkedClass: MyThirdClass = vc_from_func_args as? MyThirdClass {
//It only hits here if it is MyThirdClass
}
Also you are tying to instantiate a little bit wired :-)
Change this
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
To something like this
vc.isMemberOfClass(MyClass)
You don't need to create an instance to check if another object is kind of a class :-) But just use my code from above... Its even better than this one
I discovered the same issue a little while ago, and ended up writing a downcast global function, and a protocol called Castable that includes the asType function:
protocol Castable: class {
func asType<T>(t: T.Type, defaultValue: T?, file: StaticString,
function: StaticString, line: UWord) -> T?
}
Basically, you can take any class object and write myObject.asType(newType) and it performs the cast. If the cast fails, it logs the failure to the console, reporting the types you were casting to and from, and the file name, method name, and line number where the method was called. I use it for debugging.
At any rate, the way that the asType and downcast functions are written, you can pass the type that you are casting to as a named variable, which is what your original question wanted to do.
The complete code for downcast, the Castable protocol, and the asType function are available at this link.
I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a #property that can only be a GPUImageOutput subclass that implements GPUImageInput.
(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)
Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput
You can't declare typealias like that.
The best we can do is something like this:
class MyClass {
private var filter:GPUImageOutput
init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func someMethod() {
let output = self.filter
let input = self.filter as GPUImageInput
output.someOutputMethod()
input.someInputMethod()
}
}
In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:
func foo(vc: UIViewController & UITableViewDataSource) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}
In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:
typealias GPUImageOutput = UIImage
#objc protocol GPUImageInput {
func lotsOfInput()
}
class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
{
func lotsOfInput() {
println("lotsOfInput")
}
}
// ...
var someGpuImage = GPUImageOutput()
var specificGpuImage = GPUImageOutputWithInput()
for image in [someGpuImage, specificGpuImage] {
if let specificImage = image as? GPUImageInput {
specificImage.lotsOfInput()
} else {
println("the less specific type")
}
}
UPDATE: now that I understand where/why you have these types ...
GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.
See here:
class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
...
The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.
The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)
UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.
I came up with this strategy:
import Foundation
#objc protocol SpecialProtocol {
func special()
}
class MyClass {}
class MyClassPlus: MyClass, SpecialProtocol {
func special() {
println("I'm special")
}
}
class MyContainer {
private var i: MyClass?
var test: MyClass? {
get {
return self.i
}
set (newValue) {
if newValue is SpecialProtocol {
self.i = newValue
}
}
}
}
var container = MyContainer()
println("should be nil: \(container.test)")
container.test = MyClass()
println("should still be nil: \(container.test)")
container.test = MyClassPlus()
println("should be set: \(container.test)")
(container.test as? MyClassPlus)?.special()
Outputs:
should be nil: nil
should still be nil: nil
should be set: Optional(main.MyClassPlus)
I'm special
(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)
#rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.
You can use typealias keyword. Here is how to do it:
typealias MyNewType = MyExistingType
It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say
private typealias MyNewType = MyExistingType
That makes MyNewType is only accessible the context that is defined in.
I read a few tutorial about swift, and have made simple counter app, but I want modify it to leave controller clean and logic move to external class:
In ViewController.swift i have
class ViewController: UIViewController {
var counter: Counter?
override func viewDidLoad() {
super.viewDidLoad()
counter = Counter(label: labelCounter)
counter.renderInit()
}
}
and I have Counter class:
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
...
}
}
Unfortunatelly in controller on line counter.renderInit() I see error message:
'Counter?' does not have a member named 'renderInit'
Change counter.renderInit() to one of these:
counter?.renderInit()
counter!.renderInit()
Counter? is an optional type. You need to unwrap it. Doing ? will ignore the method if counter is nil, and ! will force it to unwrap and throw an error if it does not exist.
Check out this page in Swift's documentation for more on optionals.
ok, I move step forward and have another problem with call method object.
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
label?.text = String(counter)
}
}
I got "Cannot assign to the result of this expression." (for line where I want assign number converted to string, to label text.
I read documentation and couple of tutorials, but with learning new thing it is good to look on example and to it in similar way. Unfortunatelly Swift is young language and there are not many examples. (I am Ruby language programmer).