What I did:
Deleted derived data, Restarted xcode, Editor->Refresh all views
I am getting Designables build failed in storyboard when I click on Editor->Refresh all views.
Please check the following code. What am I missing? Textfiled is not getting updated in the storyboard when I change the #IBInspectable value from the attributes inspector.
import UIKit
import QuartzCore
#IBDesignable
class BorderedFloatingTF: UITextField {
required init?(coder aDecoder:NSCoder) {
super.init(coder:aDecoder)
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 0)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
}
// properties..
#IBInspectable var enableTitle : Bool = false
#IBInspectable var borderColor: UIColor = UIColor.white {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: Int = 1 {
didSet {
layer.borderWidth = CGFloat(borderWidth)
}
}
#IBInspectable var placeHolderColor: UIColor = UIColor.white {
didSet {
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSForegroundColorAttributeName: placeHolderColor])
}
}
fileprivate func setup() {
borderStyle = UITextBorderStyle.none
layer.borderWidth = CGFloat(borderWidth)
layer.borderColor = borderColor.cgColor
placeHolderColor = UIColor.white
}
}
Try this
import QuartzCore
#IBDesignable
class BorderedFloatingTF: UITextField {
required init?(coder aDecoder:NSCoder) {
super.init(coder:aDecoder)
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 0)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
// properties..
#IBInspectable var enableTitle : Bool = false
#IBInspectable var borderColor: UIColor = UIColor.white {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: Int = 1 {
didSet {
layer.borderWidth = CGFloat(borderWidth)
}
}
#IBInspectable var placeHolderColor: UIColor = UIColor.white {
didSet {
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSForegroundColorAttributeName: placeHolderColor])
}
}
func setup() {
borderStyle = UITextBorderStyle.none
layer.borderWidth = CGFloat(borderWidth)
layer.borderColor = borderColor.cgColor
placeHolderColor = UIColor.white
}
}
Your IBDesignables & IBInspectables both are working fine.
I had this problem with a custom class loading from a xib. Finally stumbled on this solution (had to use the correct bundle for the class, rather than Bundle.main):
#IBDesignable
class DataEntryView: UIView {
#IBOutlet var contentView: DataEntryView!
#IBInspectable var hasError : Bool = false
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
private func commonInit() {
let bundle = Bundle.init(for: type(of: self))
bundle.loadNibNamed("DataEntryView", owner: self, options: nil)
addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
contentView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
contentView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
}
}
I had the same problem on a project. I did the following steps to fix the issue.
In the custom IBDesignable view, make sure that you override both methods
required init?(coder aDecoder: NSCoder)
override init(frame: CGRect)
Add following to your Runpath Search Paths in Build Settings
#loader_path/Frameworks
$(CONFIGURATION_BUILD_DIR)
Clean your project, delete derived data.
That should be all fine.
In my project i was missing for setting its value Type. Your problem is not about that. But for other people who are having same issue with me, I wanted to explain.
Example:
I was writing like below:
#IBInspectable public var rowOrColoumnCount = 0
But It should have been like:
#IBInspectable public var rowOrColoumnCount : Int = 0
Check whether it's correct Module specified in "Module" field. It's specified "Template1" now, is it correct?
try also to make all #IBInspectable properties as public
Try to leave only this part:
#IBDesignable
class BorderedFloatingTF: UITextField {
// properties..
#IBInspectable var enableTitle : Bool = false
#IBInspectable var borderColor: UIColor = UIColor.white {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: Int = 1 {
didSet {
layer.borderWidth = CGFloat(borderWidth)
}
}
#IBInspectable var placeHolderColor: UIColor = UIColor.white {
didSet {
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSForegroundColorAttributeName: placeHolderColor])
}
}
}
Change the custom class back to what it was (UITextField?)
Only set the File's Owner in the xib to BorderedFloatingTF
Related
I have the following class that creates a UIButton subclass. I have set this up so the button automatically constrains itself to the superview and sets it's height.
#IBDesignable
class PrimaryButtonConstrained: UIButton {
#IBInspectable
var cornerRadius: CGFloat = 8 {
didSet {
setupPrimaryConstrainedButton()
}
}
#IBInspectable
var borderWidth: CGFloat = 0 {
didSet {
setupPrimaryConstrainedButton()
}
}
#IBInspectable
var topConstraint: CGFloat = 100 {
didSet {
layoutSubviews()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupPrimaryConstrainedButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupPrimaryConstrainedButton()
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
override func layoutSubviews() {
super.layoutSubviews()
translatesAutoresizingMaskIntoConstraints = false
leadingAnchor.constraint(equalTo: superview!.layoutMarginsGuide.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: superview!.layoutMarginsGuide.trailingAnchor).isActive = true
heightAnchor.constraint(equalToConstant: 50).isActive = true
topAnchor.constraint(equalTo: superview!.topAnchor, constant: topConstraint).isActive = true
}
func setupPrimaryConstrainedButton() {
setTitleColor(.white, for: .normal)
setTitleColor(.gray, for: .disabled)
setTitleColor(.orange, for: .highlighted)
layer.borderWidth = borderWidth
layer.cornerRadius = cornerRadius
if isEnabled {
backgroundColor = .orange
} else {
backgroundColor = .gray
}
}
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupPrimaryConstrainedButton()
}
}
The button constraints itself correctly in interface builder when you set the button class, however the frame of the button does not update to the buttons new location as shown.
Does anyone have any ideas on how to fix the frame so it still encloses the button?
I created a custom button that is designable in storyboard, and i styled the button select state. Now when i tap the button blue square appear above that. This is my code:
Swift version: 4.0
#IBDesignable class CircularButton: UIButton {
#IBInspectable var borderColor: UIColor = UIColor(red: 255.0, green: 153.0, blue: 0.0, alpha: 1.0)
var borderWidth: CGFloat = 3.0
#IBInspectable var bgColor: UIColor? {
didSet {
updateButton()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpButton()
self.addTarget(self, action: #selector(btnTapp), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpButton() {
self.backgroundColor = self.bgColor
let cornerRadius = (self.frame.size.width + self.frame.size.height) / 4
self.layer.cornerRadius = cornerRadius
}
#objc func btnTapp(_ sender: UIButton) {
if isSelected == false {
isSelected = !isSelected
} else if isSelected == true {
isSelected = !isSelected
}
}
override var isSelected: Bool {
didSet {
updateState(state: isSelected)
}
}
func updateState(state: Bool) {
if state == false {
self.layer.borderWidth = 0.0
self.layer.borderColor = borderColor.cgColor
print("fls")
} else if state == true {
self.layer.borderWidth = 3.0
self.layer.borderColor = borderColor.cgColor
print("tre")
}
}
}
these are image of my problem in selected state:
is unselect state
TylerTheCompiler wrote:
If your button's type is "System" in your storyboard, try setting it to "Custom".
Then, original poster wrote:
so tnx comrade the problem is solved
(Just marking this as Answered, so it doesn't look unanswered in the future.)
I want to ask you, is there any way to make a swift button has the design like this when clicked, and this when not. I want to give me some proposal or anything to do it.
Create a Custom UIView Class and copy the below code and try it :)
Don't forget to change a couple of values for customizations
class TestView: UIView {
private let selectorView: UIView = UIView()
private let trackView: UIView = UIView()
private var selectorViewLeadingConstraint: NSLayoutConstraint!
private var selectorViewTrailingConstraint: NSLayoutConstraint!
private var tapGesture: UITapGestureRecognizer!
public var isSelected: Bool = false {
didSet {
self.setIsSelected(isSelected)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
addTrackView()
addSelectorView()
addTapGestures()
}
private func addTapGestures() {
tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
addGestureRecognizer(tapGesture)
}
private func addTrackView() {
addSubview(trackView)
trackView.translatesAutoresizingMaskIntoConstraints = false
trackView.layer.cornerRadius = 5
trackView.backgroundColor = .gray
NSLayoutConstraint.activate([
trackView.leadingAnchor.constraint(equalTo: leadingAnchor),
trackView.trailingAnchor.constraint(equalTo: trailingAnchor),
trackView.heightAnchor.constraint(equalToConstant: 10),
trackView.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
private func addSelectorView(){
addSubview(selectorView)
selectorView.translatesAutoresizingMaskIntoConstraints = false
selectorView.backgroundColor = .green
selectorViewLeadingConstraint = selectorView.leadingAnchor.constraint(equalTo: leadingAnchor)
selectorViewLeadingConstraint.priority = UILayoutPriority(500)
selectorViewTrailingConstraint = selectorView.trailingAnchor.constraint(equalTo: trailingAnchor)
selectorViewTrailingConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
selectorViewLeadingConstraint,
selectorViewTrailingConstraint,
selectorView.topAnchor.constraint(equalTo: topAnchor),
selectorView.bottomAnchor.constraint(equalTo: bottomAnchor),
selectorView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5),
])
}
override func layoutSubviews() {
super.layoutSubviews()
selectorView.layer.cornerRadius = selectorView.frame.height/2
}
#objc func tapHandler() {
isSelected = !isSelected
}
private func setIsSelected(_ isSelected: Bool) {
selectorViewTrailingConstraint.priority = isSelected ? .defaultHigh : .defaultLow
UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
self.selectorView.backgroundColor = isSelected ? .green : .green
}
}
}
I am trying to set the cornerRadius of a UIButton so that it looks like circular , but I am not able to do it .
Here is the code for what I have tried.
func addColorButton() {
let colorButton : UIButton = {
let cb = UIButton()
cb.translatesAutoresizingMaskIntoConstraints = false
cb.backgroundColor = .black
return cb
}()
view.addSubview(colorButton)
colorButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
colorButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
colorButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5).isActive = true
colorButton.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5).isActive = true
colorButton.layer.cornerRadius = 0.5 * colorButton.bounds.size.width
print(colorButton.bounds.width)
colorButton.clipsToBounds = true
}
override func viewDidLoad() {
super.viewDidLoad()
addColorButton()
}
The buttons bounds/frame won't set until autolayout completed.
You could update,
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Update corner radius here
colorButton.layer.cornerRadius = 0.5 * colorButton.bounds.size.width
}
I think you need to use masksToBounds for this.
colorButton.layer.masksToBounds = true
You can create simple subclass which can use whenever you need
I just show reference of UIButton with shadow and round corner , You can make it IBInspectable to use these properties from storyboard though .
class UIButtonShadow: UIButton {
var yPos : CGFloat = 2 {
didSet {
addBehavior()
}
}
var radius : CGFloat = 2 {
didSet {
addBehavior()
}
}
var cornerRadius : CGFloat = 18 {
didSet {
addBehavior()
}
}
override var bounds: CGRect {
didSet {
addBehavior()
}
}
override var frame: CGRect{
didSet{
addBehavior()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addBehavior()
}
override func awakeFromNib() {
super.awakeFromNib()
addBehavior()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func addBehavior() {
self.layer.cornerRadius = cornerRadius
let shadowPath = UIBezierPath(roundedRect:self.bounds,cornerRadius:self.layer.cornerRadius)
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width:0.0,height:yPos)
self.layer.shadowOpacity = 0.3
self.layer.shadowPath = shadowPath.cgPath
self.layer.shadowRadius = radius
}
}
Changing the colours is pretty straightforward, but is it possible to change the border of all unselected dots?
Ex:
dot.layer.borderWidth = 0.5
dot.layer.borderColor = UIColor.blackColor()
Yes This can be done..
Actually its pretty simple.
For iOS 14 Apple has introduced a great customization, where you can set custom images and even set background
let pageControl = UIPageControl()
pageControl.numberOfPages = 5
pageControl.backgroundStyle = .prominent
pageControl.preferredIndicatorImage = UIImage(systemName: "bookmark.fill")
pageControl.setIndicatorImage(UIImage(systemName: "heart.fill"), forPage: 0)
For prior to iOS 14:-
The Pagecontrol is composed of many Subviews which you can access. self.pageControl.subviews returns you [UIView] i.e array of UIView's.
After you get a single view you can add border to it , change its borderColor, change its border width, transform the dot size like scaling it.. All those properties that a UIView has can be used.
for index in 0..<array.count{ // your array.count
let viewDot = weakSelf?.pageControl.subviews[index]
viewDot?.layer.borderWidth = 0.5
viewDot?.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
if (index == indexPath.row){ // indexPath is the current indexPath of your selected cell or view in the collectionView i.e which needs to be highlighted
viewDot?.backgroundColor = UIColor.black
viewDot?.layer.borderColor = UIColor.black.cgColor
}
else{
viewDot?.backgroundColor = UIColor.white
viewDot?.layer.borderColor = UIColor.black.cgColor
}
}
and it looks like this
And remember you do not need to set weakSelf?.pageControl.currentPage = indexPath.row.Do let me know in case of any problem.. Hope this solves your problem.
All the best
iOS 14 allows setting indicator image with SFSymbol here's my subclassing of UIPageControl
class BorderedPageControl: UIPageControl {
var selectionColor: UIColor = .black
override var currentPage: Int {
didSet {
updateBorderColor()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
currentPageIndicatorTintColor = selectionColor
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func updateBorderColor() {
if #available(iOS 14.0, *) {
let smallConfiguration = UIImage.SymbolConfiguration(pointSize: 8.0, weight: .bold)
let circleFill = UIImage(systemName: "circle.fill", withConfiguration: smallConfiguration)
let circle = UIImage(systemName: "circle", withConfiguration: smallConfiguration)
for index in 0..<numberOfPages {
if index == currentPage {
setIndicatorImage(circleFill, forPage: index)
} else {
setIndicatorImage(circle, forPage: index)
}
}
pageIndicatorTintColor = selectionColor
} else {
subviews.enumerated().forEach { index, subview in
if index != currentPage {
subview.layer.borderColor = selectionColor.cgColor
subview.layer.borderWidth = 1
} else {
subview.layer.borderWidth = 0
}
}
}
}
}
Extension for set pagecontrol indicator border / Swift 3
extension UIImage {
class func outlinedEllipse(size: CGSize, color: UIColor, lineWidth: CGFloat = 1.0) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.setStrokeColor(color.cgColor)
context.setLineWidth(lineWidth)
let rect = CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)
context.addEllipse(in: rect)
context.strokePath()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
USE:
let image = UIImage.outlinedEllipse(size: CGSize(width: 7.0, height: 7.0), color: .lightGray)
self.pageControl.pageIndicatorTintColor = UIColor.init(patternImage: image!)
self.pageControl.currentPageIndicatorTintColor = .lightGray
If anybody wants to CustomUIPageControl, then might need this
#IBDesignable
class CustomPageControl: UIView {
var dotsView = [RoundButton]()
var currentIndex = 0
#IBInspectable var circleColor: UIColor = UIColor.orange {
didSet {
updateView()
}
}
#IBInspectable var circleBackgroundColor: UIColor = UIColor.clear {
didSet {
updateView()
}
}
#IBInspectable var numberOfDots: Int = 7 {
didSet {
updateView()
}
}
#IBInspectable var borderWidthSize: CGFloat = 1 {
didSet {
updateView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func updateView() -> Void {
for v in self.subviews{
v.removeFromSuperview()
}
dotsView.removeAll()
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.horizontal
stackView.distribution = UIStackView.Distribution.fillEqually
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
NSLayoutConstraint.activate([
stackView.heightAnchor.constraint(equalToConstant: 20.0)
])
stackView.removeFullyAllArrangedSubviews()
for i in 0..<numberOfDots {
let button:RoundButton = RoundButton(frame: CGRect.zero)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 10.0),
button.widthAnchor.constraint(equalToConstant: 10.0),
])
button.tag = i
button.layer.borderWidth = 1
// button.backgroundColor = circleBackgroundColor
// button.layer.borderWidth = borderWidthSize
// button.layer.borderColor = circleColor.cgColor
button.addTarget(self, action:#selector(self.buttonClicked), for: .touchUpInside)
stackView.addArrangedSubview(button)
dotsView.append(button)
}
}
func updateCurrentDots(borderColor : UIColor, backColor : UIColor, index : Int){
for button in dotsView{
if button == dotsView[index]{
button.backgroundColor = backColor
button.layer.borderColor = borderColor.cgColor
}else{
button.backgroundColor = .clear
button.layer.borderColor = borderColor.cgColor
}
}
}
#objc func buttonClicked() {
print("Button Clicked")
}
class RoundButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
override func layoutSubviews() {
self.layer.cornerRadius = self.frame.size.width / 2
}
}
extension UIStackView {
func removeFully(view: UIView) {
removeArrangedSubview(view)
view.removeFromSuperview()
}
func removeFullyAllArrangedSubviews() {
arrangedSubviews.forEach { (view) in
removeFully(view: view)
}
}
}
You can use either Programmatically or Stoaryboard
To update the current dots calls this function
self.pageControl.updateCurrentDots(borderColor: .white, backColor: .white, index:1)