Is it possible to set a design-time text for UILabel (or image if using UIImageView) on iOS 8? if so, how to?
Basically what I need is not to empty all of my labels before compiling so that it doesn't show dummy data before loading from the network the actual data. An algorithm to programatically clear all outlets isn't really a good solution as it is unnecessary code.
You could try subclassing the classes you want to have design-time attributes. Here is an example of that for UILabel:
import UIKit
class UIPrototypeLabel: UILabel {
#IBInspectable var isPrototype: Bool = false
override func awakeFromNib() {
if (isPrototype) {
self.text = "test"
}
}
Then, in IB, you will see isPrototype, and you can set it to true or false.
You can also change the default from false to true in the isPrototype:Bool = false line if you want. You can also change what happens if isPrototype is true. I had it make the text "test" so I could see feedback when testing this out, so you could change it to nil or "" or whatever.
You can also just eschew the isPrototype bool and have this class always reset the text. I just thought the IBInspectable attribute was cool, but if you just want this class to always clear the label text then you would just delete the bool and the check and just self.text=nil every time.
The con to this approach is you need to make all of your labels UIPrototypeLabel to get this functionality.
There is a second, scarier approach, that will add this functionality to all of your UILabels, and that is extending UILabel.
import ObjectiveC
import UIKit
// Declare a global var to produce a unique address as the assoc object handle
var AssociatedObjectHandle: UInt8 = 0
extension UILabel {
#IBInspectable var isPrototype:Bool {
get {
var optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedObjectHandle)
if let object:AnyObject = optionalObject {
return object as! Bool
} else {
return false // default value when uninitialized
}
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
public override func awakeFromNib() {
if (isPrototype) {
self.text = "test"
}
}
}
credit to Is there a way to set associated objects in Swift? for some of that code
Related
I am writing a custom UITextField right now in Swift, and encountered the following:
class MyField: UITextField {
open override var text: String? {
didSet {
// some logic that normalizes the text
}
}
private func setup() { //called on init
addTarget(self, action: #selector(textEditingChanged(_:)), for: .editingChanged)
}
#objc func textEditingChanged(_ sender: UITextField) {
}
}
Now, when testing this, I observed that, when the user is typing something, textEditingChanged is called, but text.didSet is not. Neither is text.willSet, for that matter. However, in textEditingChanged, the textfield's text will already be updated to the new value.
Can anyone explain what is going on here? Is Swift circumventing the setter on purpose? How am I supposed to know when/if the setter is called, is there any logic to this in UIKit?
The text property of the UITextField is only for external use, when you are typing UIKit handles the update internally. Can't really tell whats going on under the hood, as we do not have access to UIKit implementation but one possible answer to the question is that UITextField is using an other internal variable for storing the text property. When you are getting the text property, you are actually getting that internal value, and when you are setting it, you are setting that internal value also. Of course under the hood it is a bit more complicated but it may look something like this(SampleUITextField represents the UITextField):
// This how the UITextField implementation may look like
class SampleUITextField {
private var internalText: String = ""
var text: String {
get {
internalText
} set {
internalText = newValue
}
}
// This method is just to demonstrate that when you update the internalText didSet is not called
func updateInternalTextWith(text: String) {
internalText = text
}
}
And when you subclass it it looks like:
class YourTextField: SampleUITextField {
override var text: String {
didSet {
print("DidSet called")
}
}
}
Now when you set the text property directly the didSet is called because the text value updates. You can check it:
let someClass = YourTextField()
someClass.text = "Some new text"
// didSet is called
But now when you call updateInternalTextWith the didSet is not called:
let someClass = YourTextField()
someClass.updateInternalTextWith(text: "new text")
// didSet is not called
That's because you are not updating the text value directly, but just the internal text property. A similar method is called to update the internal text variable of the UITextField when you are typing, and that's the reason the didSet is not called in your case.
For that reason, it is not enough to override the text variable when we want to be notified when the text properties changes, and we need to use delegate(UITextFieldDelegate) methods or notifications (textFieldDidChange) to catch the actual change.
What kind of formatting are you trying to perform on the UITextField?
Shouldn't it be the responsibility of a ViewModel (or any model managing the business logic for the view containing this text field) instead?
Also, you shouldn't use didSet to perform changes, its mainly here to allow you to respond to changes, not chain another change.
I have problem with xcframwork.when i create xcframwork my properties not display in my main project storyboard. When i create framework it display all properties in my storyboard.
I have created Following code
#objc
public extension UIView {
// Note : Corner radius and shadow not work both side by side so you need to outlet and set layer radius
// other wise you can set layer.cornerradius in user defines
//MARK: Border COLOR
#IBInspectable
var borderColor: UIColor? {
get {
return self.borderColor
}
set {
self.layer.borderColor = newValue?.cgColor
}
}
}
1 using framework
It display all Properties
now i created xcframework. all properties not display Now i am stuck with this.
anyone help me how can i create xcframwork and display all properties like normal framework. Please help me on this.
The properties in your framework are internal. When no access modifier is specified then Swift defaults to internal.
enter image description here
so in your code:
var radTopLeft:CGFloat = 0 {
didSet {
self.setNeedsLayout()
}
}
is equivalent to:
internal var radTopLeft:CGFloat = 0 {
didSet {
self.setNeedsLayout()
}
}
Therefore no code outside the framework can access this property. In order to make it accessible to the framework consumer you have to mark the property public. i.e.
public var radTopLeft:CGFloat = 0 {
didSet {
self.setNeedsLayout()
}
}
Test it out: https://drive.google.com/file/d/1_WSGKRKNt4lytrVEuozn8W4WYRR6nPif/view?usp=sharing
Okay, this might be one of the most basic questions ever, but all answers I find use storyboard to declare an outlet for a label, textfield or whatever element that needs to be changed. I, however, don't use storyboards and write everything in code. Now I have a function setupViews, where I define a textfield:
let usernameInput = UITextField()
Now, I can perfectly set the text or placeholder or whatever inside this setupViews() class, but how can I access it outside? For example, if I have a function logIn(), I want to call usernameInput.text and use it in this function.
Someone who can point me in the right direction? Do I need to declare this textfield globally, in another file, or something else?
When I create my views in code I always associate a property with the view that has all those various display values.
I have not tested this code to see but hopefully the following will give you an idea.
import UIKit
struct {
var name: String
}
class CustomViewController : UIViewController {
// some struct which contains data for view
var customViewData : ViewDataInfo? {
didSet {
labelOnScreen.text = customViewData.name
}
}
var labelOnScreen: UILabel = {
let label = UILabel()
label.text = "Placeholder information..."
// stuff auto layout
label.translateAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
view.addSubview(label)
// set your constraints here
}
}
Without any experience with iOS, I have been asked to upgrade to Swift 4 an application that is using Swift 3. I updated the language to use in "Swift language version" and I followed the recommendations made by xCode (explicitly use #objc).
Once all errors fixed the application works, but one UI-related feature is not working anymore. The app has a custom UITextField that is used to enter a password. Depending on the value of a #IBInspectable class field of the custom text field the text is hidden or not.
The value of this variable is correctly set in MainStoryboard but when the class is instantiated this value is not set to the value in the storyboard, and as a result, the custom text field does not behave how it should. The log does not mention any error.
Does anybody experience the same problem?
I checked storyboard and class, I did not find any problems, I also compared the code of the version with Swift 3 and the one I updated with Swift 4 and there are no differences (except the #objc ones).
Are there something else to look that can help me to find why the value set in MainStoryboard is not propagated to the class?
Edit
Below is the code involved in the problem:
a variable isPassword is declared to reflect the value of the IBInspectable variable
the variable IBInspectable is declared
when awakeFromNib depending on the value of isPassword a method to either hide the text or not is called
#IBDesignable class CustomEditText: UITextField {
var isPassword = false
// Inspector variable determining if the text must be hidden or not
#IBInspectable var isAPasswordField: Bool {
get{
return self.isPassword
}
set{
self.isPassword = isAPasswordField
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setup() // Set borders, font, ...
}
override func awakeFromNib() {
super.awakeFromNib()
if isPassword { // isPassword does not reflect the value of isAPasswordField set in storyboard
self.isSecureTextEntry = true
setRevealButton() // the text is hidden
} else {
setCustomClearButton() // the text is not hidden
}
}
...
Answer
#Alladinian gave the good answer.
Below for completeness, another solution that I found:
#IBInspectable var isPasswordField: Bool = false {
didSet{
self.isPassword = isPasswordField
if isPassword {
self.isSecureTextEntry = true
setRevealButton()
} else {
setCustomClearButton()
}
}
}
The correct form would be (note the setter assignment):
#IBInspectable var isAPasswordField: Bool {
get {
return self.isPassword
}
set {
self.isPassword = newValue
}
}
since the actual value have not been (and will not be - at least 'directly') set on isAPasswordField (remember, we are implementing the setter of a computed property after all...)
Also, why don't you just use isPassword directly by marking it as inspectable, avoiding the need for an extra ivar?
PS: newValue is the default name for the to-be-set value. You can read more about it under Shorthand Setter Declaration section in the Swift Programming Language book
The following code works fine for me for newer versions:
#IBInspectable var isAPass: Bool {
get {
return self.isSecureTextEntry
}
set {
self.isSecureTextEntry = newValue
}
}
Is there any way to add section name in newly added #IBDesignable properties for better readability.
//
//MARK: - Badge
//
#IBInspectable
var badgeColor:UIColor = UIColor.darkGray {
didSet {
updateView()
}
}
#IBInspectable
var showBadgeOnIndex:Int = 0 {
didSet {
if showBadgeOnIndex >= buttons.count {
showBadgeOnIndex = 0
}
updateView()
}
}
#IBInspectable
var showAllBadge:Bool = false {
didSet {
updateView()
}
}
#IBInspectable
var hideAllBadge:Bool = true {
didSet {
updateView()
}
}
ex:
In the picture above, the view properties have the section name View.
Any help would be appreciated.
I already know that the name of the custom class will appear as the section name in the Attributes Inspector.
Sections and their titles in the Attributes inspector are generated based on what exact classes the properties in that section belong to. There is no way to change this behavior.
However, Xcode automatically groups the inspectable properties that have similar names. Those groups are separated with a line. This behavior is also visible in your example picture:
By default your classname (A name of your custom class) becomes a title for IBDesignable properties
Here is reference documents, what you want. IBInspectable / IBDesignable