Get Identifier and Hint of UIView in swift class - ios

I'm assigning an identifier to a view using storyboard and want to access it through swift class. Is there any method to do that? I'm searching it on stackoverflow but couldn't find any solution.
Few people gave solution to use tag instead of identifier but I want to ask that isn't there any solution to get these hints and identifiers inside of swift class.
it gives error when ever I try to access it using accessibilityIdentifier.

You access it via accessibilityIdentifier e.g. myView.accessibilityIdentifier

The error is because you are not using the value. Just assign it to a variable an it will work.
let yourId = yourView.accessibilityIdentifier

Here is an extension
var viewIdentifier: String?{
get {
return self.accessibilityIdentifier
}
set {
self.accessibilityIdentifier = newValue
}
}

Related

Swift if let statement results in <<error type>> instead of custom object

I'm trying to use if let Swift statement to use an optional if it's not equal to nil. But for some reason, when I use it, Xcode shows the object is `<>z
I have a function which returns MyObject? and I want to check if it is nil, and if it's not I want to use it.
I'm trying to do it like this:
if let anObject = self.myFunc() {
anObject //Xcode shows that anObject is not MyObject but <<error type>>
}
(I'm using SwiftUI if that matters)
Does anyone knows why?
The if let is not allowed within body as is, so if you just need to conditionally shown some view on result of function, then the solution will be
if self.myFunc() != nil {
// some view here
}

how to modify UIButton's accessibilityLabel in an extension

I'm using an analytics tool which logs the accessibilityLabel of buttons.
I'm trying to find a way to update the accessibilityLabel without changing my existing code.
For normal buttons I use the titleLabel.text. For iconButtons which use their the name coming from image assets I use accessibilityLabel itself.
Some issues I faced:
can't access accessibilityLabel within its getter. Because that would recursively look for accessibilityLabel.
So I had to use another property for backing and since this was an extension I wasn't able to use stored properties. Computed properties didn't work either because it would get stuck in the same feedback loop.
Eventually I hacked my way by using accessibilityHint. It's a stored property that I have no use of...
This works! Yet I've been told and read that I shouldn't override functions in an extension as that's not reliable. So I'm wondering what I should do?
And if Swift has any mechanism that doesn't involve overriding in UIButton's extension?!
here is my code:
extension UIButton{
private var adjustAccessibilityLabel : String{
if titleLabel?.text?.isEmpty == false{
return titleLabel!.text!
}else if accessibilityHint?.isEmpty == false{
return accessibilityHint!
}else{
return "Empty"
}
}
open override var accessibilityLabel: String?{
get{
return "\(self.adjustAccessibilityLabel))"
}
set{
accessibilityHint = newValue // Hacking my way through!
}
}
}
You're fighting the system. You can achieve this using subclassing.
To avoid similar problems in the future, always have ALL your UIButton, UITableViewCell, UIViewController, etc subclassed from your own base class so you can easily make such universal changes.

How to declare a constant that can be accessed by multiple viewcontrollers in Swift?

I have an app I'm working on that has various viewcontrollers that use the same constants.
ex.
let wrongAnswerBanner = UIImageView(image: UIImage(named: "torn_banner"))
I tried declaring the constant outside the viewcontrollers but whenever I try to call the constant by using self.
self.wrongAnswerBanner.hidden = false
I get the error: Value of type 'ViewController1' has no member 'wrongAnswerBanner'. How can I declare these constants without having to redeclare them within each individual viewcontroller?
You can declare the constant outside of a class scope and access it directly from any file inside the module.
File 1
let MyConstant = "MyConstant"
class A {
}
File 2
class B {
// use MyConstant directly (eg. print(MyConstant))
}
I usually do this for UITableViewCell identifiers. I declare them on top of my UITableViewCell subclass and use them on the ViewController file. It's worth nothing though (as other developers mentioned) that UIImageView might not be a good candidate for constants. You can also make use of Enums if that makes sense for your problem.
Just try to call the constant without self
Create a struct with constants:
struct Constant {
static let SomeConstant = "hey"
}
then you can get the value from any class by
let constant = Constant.SomeConstant
According to me you have to create one AppConstant.swift file in your application ,then just add this line in AppConstant.swift file
let wrongAnswerBanner = UIImageView(image: UIImage(named: "torn_banner"))
Then in any of controller you can easily access wrongAnswerBanner without using self

"fatal error: array cannot be bridged from Objective-C"—Why are you even trying, Swift?

I have declared a Swift protocol:
protocol Option {
var name: String { get }
}
I declare multiple implementations of this protocol—some classes, some enums.
I have a view controller with a property declared as so:
var options: [Option] = []
When I try and set this property to an array of objects that implement the Option protocol in another VC's prepareForSegue, I get a runtime error:
fatal error: array cannot be bridged from Objective-C
Why doesn't this work? The compiler has all the information it needs, and I don't understand what Objective-C has to do with it at all—my project contains only Swift files, and these arrays aren't coming in or out of any framework methods that would necessitate them being bridged to NSArray.
I have found a solution. It is quite... unsatisfying, but it works. Where I set the array on the destination view controller I do:
destinationViewController.options = options.map({$0 as Option})
the compiler knows I'm passing in an Array of things that implement Option
You've let slip there a very revealing remark, which suggests the source of the issue. An "Array of things that implement Option" is not an Array of Option.
The problem is with the type of options back at the point where you create it (in prepareForSegue). You don't show that code, but I am betting that you fail to cast / type it at that point. That's why the assignment fails. options may be an array of things that do in fact happen to adopt Option, but that's not enough; it must be typed as an array of Option.
So, back in prepareForSegue, form your options like this:
let options : [Option] = // ... whatever ...
Now you will be able to assign it directly to destinationViewController.options.
Here's a quick test case (in a playground; I detest playgrounds, but they can have their uses):
protocol Option {
var name : String {get}
}
class ViewController : UIViewController {
var options : [Option] = []
}
enum Thing : Option {
var name : String {
get {
return "hi"
}
}
case Thing
}
let vc = ViewController()
let options : [Option] = [Thing.Thing]
vc.options = options // no problem
(I also tested this in an actual app with an actual prepareForSegue, and it works fine.)
I was having the same problem and fixed it marking my protocol with #objc, in your case it would look like this
#objc protocol Option {
var name: String { get }
}
Got the solution from this answer
This one also works fine
destinationViewController.options = options.map{$0}

swift check an object's property existence

I have the below UINib extension method, I was wondering if I can set a delegate for the unarchived view
public class func decodeView<T:UIView>(nibName name:String,className classType:T.Type,delegate:AnyObject) -> T {
let nib = UINib(nibName: name)
let topLevelObjects = nib.instantiateWithOwner(nil, options: nil)
let view = topLevelObjects[0] as T
view.setTranslatesAutoresizingMaskIntoConstraints(false)
//check if view.delegate exists then view.delegate = delegate
return view
}
If you're asking if Swift supports reflection, TL;DR: you need to subclass from NSObject. Else you get limited info.
In this question, Does Swift support reflection? you get a more detailed discussion about the possibilities you have.
Once you have this part cleared, an example of how to obtain a list of properties can be found in this SO Answer
Although a quick & dirty way could be just to try and access the property (using KVC) and catch the exception if it fails. Swift does NOT support Try/Catch/Finally constructs, but this nice hack allows you to write code like:
SwiftTryCatch.try({
// try something
}, catch: { (error) in
println("\(error.description)")
}, finally: {
// close resources
})

Resources