Why does my Swift button show a small blue square when tapped? - ios

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.)

Related

iOS - Custom UISwitch in Class

version: Xcode 12.3
My project uses UISwitch heavily. Therefore, I am creating a custom Class to customize it.
I would like to set UISwitch default thumbTintColor and backgroundColor, and update these colors when the switch is togged on (isOn property).
I've found a solution but it does not work when I return to the viewController. The switch doesn't retain the setting:
subviews[0].subviews[0].backgroundColor = UIColor.white
I can't set isOn because it's read-only property.
Is there anyway that I can set the value change? I want something like below in the customSwitch Class:
Switch off: thumbTintColor = UIColor.yellow, backgroundColor =
UIcolor.black
Switch on: thumbTintColor = UIColor.red, backgroundColor =
UIcolor.white
below is my custom Class, can anyone help? thanks.
import Foundation
#IBDesignable
class CustomSwitch: UISwitch {
private var previousValue = false
private var returnPreviousValue = false
override var isOn: Bool {
return returnPreviousValue ? previousValue : super.isOn
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
previousValue = isOn
addTarget(self, action: #selector(_didChange), for: .valueChanged)
}
override init(frame: CGRect) {
super.init(frame: frame)
addTarget(self, action: #selector(_didChange), for: .valueChanged)
}
override func setOn(_ on: Bool, animated: Bool) {
super.setOn(on, animated: animated)
previousValue = on
}
#objc func _didChange() {
let isOn = self.isOn
if isOn == previousValue {
return
}
returnPreviousValue = true
willChangeValue(forKey: "on")
returnPreviousValue = false
previousValue = isOn
didChangeValue(forKey: "on")
}
}
Code
#IBDesignable
class CustomSwitch: UISwitch {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialSetUp()
}
convenience init() {
self.init(frame: .zero)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.initialSetUp()
}
private func initialSetUp() {
self.addTarget(self, action: #selector(refreshUI), for: .valueChanged)
self.refreshUI()
}
#objc private func refreshUI() {
/// Be aware that this is more of a hack than a solution
/// And can break in any upcoming release
let targetSubview = self.subviews.first?.subviews.first
if self.isOn {
self.thumbTintColor = .red
targetSubview?.backgroundColor = .white
}
else {
self.thumbTintColor = .yellow
targetSubview?.backgroundColor = .black
}
}
}
Usage
let customSwitch = CustomSwitch()
self.view.addSubview(customSwitch)
customSwitch.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customSwitch.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
customSwitch.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
])

Constrained UIButton frame is not updating to the buttons constrained position in Xcode storyboard

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?

Strange issue with 2 initializers in Swift

I want to create 2 initializers for my custom button class which looks like:
protocol CustomButtonProtocol {
var title: String { get set }
var cornerRadius: CGFloat { get set }
var backgroundColor: UIColor { get set }
}
struct Button: CustomButtonProtocol {
var title: String
var cornerRadius: CGFloat
var backgroundColor: UIColor
}
class CustomButton: UIButton {
var title: String? {
didSet {
self.setTitle(title, for: .normal)
}
}
var cornerRadius: CGFloat = 0.0 {
didSet {
self.layer.cornerRadius = cornerRadius
}
}
var color: UIColor? {
didSet {
self.backgroundColor = color
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
init(with frame: CGRect, button: Button) {
super.init(frame: frame)
self.title = button.title
self.cornerRadius = button.cornerRadius
self.backgroundColor = button.backgroundColor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I want to have 2 initializers, the first one is simple init, the second one with my Button passed in, but the issue is that it changes ONLY background color but does not title and cornerRadius properties and I cannot find why it happens. Maybe I do not see something, could you please help me with finding the bug?
You have used didSet to update value to button property.
It won't call didSet while setting value inside init.
background color is getting set because you set backgroundColor by mistake in place of color
self.backgroundColor = button.backgroundColor
you should use/call below inside/outside init once.
self.setTitle(title, for: .normal)
self.layer.cornerRadius = cornerRadius
self.backgroundColor = color
Consider that property observers willSet / didSet are not called in init methods
From the documentation:
When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.
A solution is to call the code in the observers also in the init method
init(with frame: CGRect, button: Button) {
super.init(frame: frame)
self.title = button.title
self.setTitle(self.title, for: .normal)
self.cornerRadius = button.cornerRadius
self.layer.cornerRadius = self.cornerRadius
self.color = button.backgroundColor
self.backgroundColor = self.color
}
If color represents always the background color the property is actually redundant.

#IBDesignable not working in iOS swift 3

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

How to change borders of UIPageControl dots?

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)

Resources