At which order Interface Builder evaluates IBInspectables? - ios

I have custom UI element designed with a help of IBInspectables, so that I can use and modify it in the Interface Builder. There are 2 inspectables which effectively change the same property of the element but in a slightly different manner.
At the screenshot there is title that sets the title of the element, and locTitleKey that gets NSLocalizedString by the key and sets it as the title.
As I can see from passing different values to these inspectables:
If locTitleKey is specified, and title is left empty, the localised string is used
If both are specified, title is used and localized one is ignored
The question - is that behaviour predictable?
In other words, can I safely assume that the order of evaluation of my IBInspectables always be the same? (title and then locTitleKey)
To provide more context here are the snippets of code used.
title is defined in the scope of the class itself:
#IBDesignable
class StandardInputField: UIView, NibLoadable {
//...
#IBInspectable
public var title: String? {
didSet {
titleLabel.text = title
}
}
//...
}
And locTitleKey is defined as extension:
extension StandardInputField {
#IBInspectable var locTitleKey: String? {
get {
return nil
}
set(key) {
title = key?.localized
}
}
}

Related

Append filtered string array to UILables - Swift

Long title! I do apologize.
Expected Outcome: Display uniqued string value in UILabel inside a UIView in a stackView. There may be multiple UIViews in the stackView. The stackView lives inside a tableCell. wow....
The views and cells are custom and I am not allowed to create sections. I have to work with what's in the existing codebase.
Issue I am stuck at trying to get the unique optional string values into the respective UILabels. I have a working extension to get unique items from an array. But I just don't know where to implement it, to get the unique values I need.
Code:
// Sample Model Structs
struct Parent {
var child: [Child]?
}
struct Child {
var childValue: String?
}
class TableViewCell {
var stackView = UIStackView()
func configureCellFrom(parent: Parent) {
/// Other code lives in the func to use the Parent struct.
if let child = parent.child {
if child.count > 1 {
tableCell.topLabel.text = "Multiple Child Values"
tableCell.ShowChildViewsButton.isHidden = false
for value in child {
let view = CustomUIView()
view.childValue.text = value.childValue.uniqued()
self.stackView.addArrangedSubview(view)
}
}
}
}
}
extension Sequence where Element: Hashable {
func uniqued() -> [Element] {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
Above Problem: Where I placed the uniqued() method, will parse out the individual characters in the string. So I know that it is one level too deep. What's the best way to achieve my required result?
The issue there is that uniqued method uses filter method declared on Sequence which will always return an array of the Sequence.Element in the case of a String an array of characters [Character]. You can simply initialize a new string with the array or characters or improve that method to support strings as well. To make that method support strings you need to extend RangeReplaceableCollection. There is a filter method declared on RangeReplaceableCollection which returns Self therefore if you filter a string it will return another string as I showed in this answer from the same post where you found the uniqued method you've shown in your question:
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
Usage:
view.childValue.text = value.childValue?.orderedSet
Try this:
for (i, value) in child.enumerated() {
let view = CustomUIView()
view.childValue.text = value.childValue.uniqued()[i]
self.stackView.addArrangedSubview(view)
}

Extend UIView with new properties

In my app I need to add to a bunch of UIKit objects some properties; my original thought was to create a subclass of each element I needed and create the properties inside the new class but I realized that this means to write a new class for each UI element type I'm using.
In my specific case, I wanna add to some different views, such as UIImageView and UILabel, two properties called initial position and final position of type CGRect to store the initial and final position in order to use it inside a method which translates this views.
Is there any way to accomplish this without creating lots of classes?
You can do the following:
protocol Position {
var initialPosition: Int { get set }
}
It is not possible to just declare properties in extensions, so you'd need to set your get and set. You can just associate a value:
private var initialPositionKey: UInt = 0
extension Position {
var initialPosition: Int {
get {
return objc_getAssociatedObject(self, &initialPositionKey) as! Int
}
set {
objc_setAssociatedObject(self, &initialPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}
Then you extend your UIView like this:
extension UIView: Position {}
And the following works:
var view = UIView()
view.initialPosition = 5
print (view.initialPosition) // 5

How to create a new field in UITextField extension?

I am new to Swift and maybe it's a stupid question, but I can't find an answer to it.
I have created an extension:
extension UITextField {
var placeholderLabel: UILabel {
get {
return self.placeholderLabel
}
set {
self.placeholderLabel = newValue
}
}
}
When the property is set, the application crashes.
You can't have a stored property in extension.
Extensions are not allowed to add a property to existing class because adding a property structure of the class will change. And because Objective C, Swift or any other programming language that am aware of could not afford it, it won't allow you to add the stored property to extension.
Isn't there any work around then ??
This is what you can do to save the label as stored property in your extension :)
import Foundation
import UIKit
fileprivate var ascociatedObjectPointer : UInt8 = 99
extension UITextField {
var myLabel : UILabel {
get {
return objc_getAssociatedObject(self, &ascociatedObjectPointer) as! UILabel
}
set {
objc_setAssociatedObject(self, &ascociatedObjectPointer, myLabel, .OBJC_ASSOCIATION_RETAIN)
}
}
}
How it works ??
Simple by writing setter and getter for the variable which you are posing or pretending to be stored property and by internally holding a pointer which has nothing to do with the existing class, hence it won't affect the structure of existing class.
Hope it helps.
You can use NSMapTable like this:
extension UITextField {
private static var placeholderLabelMap: NSMapTable<UITextField, UILabel> = .weakToStrongObjects()
var placeholderLabel: UILabel? {
get {
return UITextField.placeholderLabelMap.object(forKey: self)
}
set {
UITextField.placeholderLabelMap.setObject(newValue, forKey: self)
}
}
}
The advantage of Sandeep's answer might be thread safety. You can see this Stack Overflow topic for comparison between the approaches.

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 {}

Swift Variable Constraints

Is it possible in the current iteration of Swift (2.1) to add constraints on a variable type?
If I have a class Element
class Element: NSObject {
var type: ElementType
init(type: ElementType) {
self.type = type
}
}
with an enum ElementType
enum ElementType {
case Cell
case Header
case Footer
case Decoration(kind: String)
}
in some other class if I have a variable of type Element, is it at all possible to put constraints like:
var element: Element? where Self == .Header
or instead would I have to override didSet
var element: Element? {
didSet {
if let value = element where value.type == Decoration {
element = Optional.None
}
}
}
I'm sure it's not possible, the generic system isn't as powerful as I would like (being able to only constrain extensions by protocol and class inheritance for example and no variadic generic parameters).
Type constraints and types checks are a compilation feature but you want to check the runtime value of an object.
I am pretty sure that's impossible. You will have to use a runtime check (e.g. willSet or didSet).
Or, instead of enforcing the type by the value of a type atrribute, enforce it with a real type, e.g.
subclassing
class Header: Element
or make Element a protocol implemented by four separate classes:
protocol Element {
}
class Header: Element
(you can use protocol extension for shared functions).

Resources