So I'm fairly new to the whole UIAppearance approach to doing things, and doing it with swift. Hurray for not a ton of documentation out there.
I'm trying to set my border radius through UIAppearance with something along the lines of:
CircleButton.appearance.roundBorderRadius = 9
My CircleButton class implementation:
public class CircleButton : UIButton{
#nonobjc var roundBorderRadius: CGFloat? {
get { return self.layer.cornerRadius }
set {
self.layer.cornerRadius = newValue!
}
}
}
And I hook everything up in Storyboard to a ViewController that contains a CircleButton. No Compilation or Build errors.
However, at runtime I'm getting a:
"Thread 1: EXC_BAD_ACCESS" error on:
CircleButton.appearance.roundBorderRadius = 9
Any advice?
Remove #nonobjc and add dynamic, then change the type from CGFloat? to CGFloat and remove the ! after newValue in the setter, like so:
public class CircleButton: UIButton {
dynamic var roundBorderRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
}
Related
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
I am currently at the start of a project and trying to make some universal inspectable/designable variables for UIViews, UIButtons, and UIImageViews to make designing in InterfaceBuilder easier. For example:
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
One suggestion I've seen to accomplish this is:
extension UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
}
However, the problem with this is that now all subclasses of UIView, show the inspectable properties, which is problematic when I will only be using these properties on maybe three classes, and I add more properties like border width/color, shadow radius, opacity, offset, and color, and these take up a lot of the inspector pane in IB.
I'd like something like this:
#IBDesignable class MyView: UIView, MyDesignable {}
#IBDesignable class MyButton: UIButton, MyDesignable {}
#IBDesignable class MyImageView: UIImageView, MyDesignable {}
protocol MyDesignable {}
extension MyDesignable where Self: UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
...
Unfortunately, #IBInspectable springs the error Only instance properties can be declared #IBInspectable.
I also tried:
#IBDesignable class MyView: UIView {}
#IBDesignable class MyButton: UIButton {}
#IBDesignable class MyImageView: UIImageView {}
private extension UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
...
}
However, other UIView subclasses still showed the inspectable properties.
Hopefully the question and intent behind it make sense. Please let me know if you have any suggestions. Thank you!
I'm trying to apply IBInspectable to all types of view like UIView, UIButton, UITextField, UIImageView, etc.
Here's what I did:
#IBDesignable
class BaseView: UIView
{
override func layoutSubviews() {
super.layoutSubviews()
updateCornerRadius()
}
#IBInspectable var rounded: Bool = false {
didSet {
updateCornerRadius()
}
}
#IBInspectable var cornerRadius: CGFloat = 4 {
didSet {
updateCornerRadius()
}
}
private func updateCornerRadius() {
layer.cornerRadius = rounded ? cornerRadius : 0
}
}
It is working fine with all UIView using BaseView subclasses in storyboard, but how can I use this for buttons, text fields, image views, etc?
May be using protocols or extensions to avoid repeating this code to all other types of views...
You should use an extension instead of a subclass, and access the layer property directly:
extension UIView
{
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius;
}
set(value) {
layer.cornerRadius = value;
}
}
var rounded: Bool {
return layer.cornerRadius > 0.0;
}
}
Note: Since you're not implementing drawRect using #IBDesignable is needless. You can't implement roundedas a settable property this way, but you can have a read-only property for that.
It is the subClass of UIView. To use the these class you have to create subClass of UIImage, UIButton etc.
So create subclasses of each component like this
class ButtonSubClass: UIButton {}// for button subclass
class ImageSubClass: UIImage {}// for image subclass
I am trying to use IBInspectable to add borders to my views.
extension UIView {
private func getBorder(integer: Int) -> UIRectEdge {
if integer == 1 {
return .top
} else if integer == 2 {
return .left
} else if integer == 3 {
return .right
} else if integer == 4 {
return .bottom
}
return .all
}
#IBInspectable var border: Int? {
get {
return self.border
}
set (value) {
self.border = value
for v in addBorder(edges: self.getBorder(integer: self.border!)) {
self.addSubview(v)
}
}
}
#IBInspectable var borderColor: UIColor? {
get {
return self.borderColor
}
set (value) {
self.borderColor = value //EXC_BAD_ACCESS here
for v in addBorder(edges: self.getBorder(integer: self.border!), color: borderColor!) {
self.addSubview(v)
}
}
}
private func addBorder(edges: UIRectEdge, color: UIColor = UIColor.white, thickness: CGFloat = 1) -> [UIView] {
...
}
}
The crash occurs on the line self.borderColor = value (in the set for the borderColor).
All it says in the debug log is (lldb). The crash itself says:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff53cc5fe8)
Here is my storyboard:
How can I fix this issue? Thanks!
You have an infinite recursion there, that is causing the crash. Basically within the setter of borderColor you're calling the setter for the same property, resulting the infinite recursion.
This happens because class extensions are not allowed to have stored properties, so Swift doesn't generate a backstore for your property, instead it treats it like a computed property, and calls the setter whenever you try to set the property.
There are two solutions that I can think of at this time, that will solve your problem:
Subclass UIView, add the two properties there, update the class in IB to match the name of your new class.
Use associated objects in your UIView accessors (objc_setAssociatedObject()/ objc_getAssociatedObject()) instead of direct iVar reference. You will not need to subclass and to update your xibs, however this solution is a little bit messier than the first one.
I am trying to extend the UIButton class by adding a cornerRadius property which can be changed at the design time without having to build the app. I am using the following extension class:
import UIKit
#IBDesignable
extension UIButton {
#IBInspectable var cornerRadius :CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
}
But when I make a change of the property cornerRadius in the Storyboard I do not see the change happening live! Am I missing something!
Extensions don't honor the IBDesignable qualifier. Only actual subclasses do. Annoying but true.
try this code:
#IBDesignable extension UIView {
#IBInspectable var borderColor:UIColor? {
set {
layer.borderColor = newValue!.CGColor
}
get {
if let color = layer.borderColor {
return UIColor(CGColor:color)
}
else {
return nil
}
}
}
}
this will show effect on runtime