I customized a button and I wanted to make it designable in the storyboard.
If I set the background to an image which is stored in the assets folder, the storyboard doesn't show anything. I still get a transparent button. Any ideas how i can fix this?
import UIKit
#IBDesignable class AnswerButton: UIButton {
private static let BTN_NORMAL_IMAGE_NAME = "ButtonNormal"
private static let BTN_ANSWERED_IMAGE_NAME = "ButtonAnswered"
enum AnswerState : Int {
case normal = 0
case answered
case correctAnswered
case wrongAnswered
}
var answerState: AnswerState = .normal {
didSet {
if answerState == .normal {
self.answerLabel.textColor = UIColor.white
self.setBackgroundImage(UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME), for: .normal)
} else if answerState == .answered {
self.answerLabel.textColor = UIColor.black
self.setBackgroundImage(UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME), for: .normal)
} else if answerState == .correctAnswered {
self.answerLabel.textColor = UIColor.blue
self.setBackgroundImage(UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME), for: .normal)
}
}
}
#IBInspectable var myAnswerState: Int {
get {
return self.answerState.rawValue
}
set (value) {
self.setAnswerState(AnswerState(rawValue: value) ?? .normal)
}
}
#IBInspectable var letter: String = "" {
didSet {
self.letterLabel.text = String(letter.first!) + ":"
}
}
#IBInspectable var answer: String = "" {
didSet {
self.answerLabel.text = answer
}
}
public let letterLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = UIColor.orange
label.font = UIFont(name: "Arial Rounded MT Bold", size: 24)
return label
}()
public let answerLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = UIColor.white
label.font = UIFont(name: "Arial Rounded MT Bold", size: 22)
return label
}()
override func layoutSubviews() {
super.layoutSubviews()
self.letterLabel.frame = CGRect(x: 40, y: 0, width: 30, height: self.frame.height)
self.answerLabel.frame = CGRect(x: 80, y: 0, width: 310, height: self.frame.height)
}
private func setAnswerState(_ state: AnswerState) {
self.answerState = state
}
private func setLetter(_ letter: String) {
self.letter = letter
}
private func setAnswer(_ answer: String) {
self.answer = answer
}
init(letter: String, selectionState: AnswerState, answer: String) {
super.init(frame: CGRect(x: 0, y: 0, width: 420, height: 47))
self.sharedInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.sharedInit()
}
#objc
func touchUpInside(sender: UIButton) {
self.setAnswerState(.answered)
}
func sharedInit() {
self.adjustsImageWhenHighlighted = false
self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
self.setLetter(letter)
self.setAnswer(answer)
self.setAnswerState(.normal)
self.addSubview(self.letterLabel)
self.addSubview(self.answerLabel)
}
override func prepareForInterfaceBuilder() {
self.sharedInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.sharedInit()
}
}
an image with the name ButtonBackground lives in the assets folder.
You have a few issues in your code...
First, to get images to load at design-time / #IBDesignable, you need to tell Interface Builder where to get them:
let dynamicBundle = Bundle(for: AnswerButton.self)
let img = UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil)
Unless you're doing something out-of-the-ordinary with your bundle structures, you can use that both at design-time and run-time. That is, you don't conditional code.
Next, in your #IBInspectable var letter / didSet:
#IBInspectable var letter: String = "" {
didSet {
self.letterLabel.text = String(letter.first!) + ":"
}
}
make sure your string is not empty or it will crash:
#IBInspectable var letter: String = "" {
didSet {
if !letter.isEmpty {
self.letterLabel.text = String(letter.first!) + ":"
}
}
}
Next, your sharedInit() func will be called when you change an #IBInspectable value, so don't call this:
self.setAnswerState(.normal)
or your button will never reflect any other value.
And, the way you're setting frames for your labels looks problematic -- if the button width is less than 390-pts, the answerLabel won't fit.
Lastly, I'd suggest loading your two images at init, instead of re-loading every time the state changes.
Here's an update to your class:
#IBDesignable class AnswerButton: UIButton {
private static let BTN_NORMAL_IMAGE_NAME = "ButtonNormal"
private static let BTN_ANSWERED_IMAGE_NAME = "ButtonAnswered"
private var normalImage: UIImage!
private var answeredImage: UIImage!
enum AnswerState : Int {
case normal
case answered
case correctAnswered
case wrongAnswered
}
var answerState: AnswerState = .normal {
didSet {
switch answerState {
case .answered:
self.answerLabel.textColor = UIColor.black
self.setBackgroundImage(answeredImage, for: .normal)
case .correctAnswered:
self.answerLabel.textColor = UIColor.blue
self.setBackgroundImage(answeredImage, for: .normal)
case .wrongAnswered:
self.answerLabel.textColor = UIColor.red
self.setBackgroundImage(answeredImage, for: .normal)
default:
self.answerLabel.textColor = UIColor.white
self.setBackgroundImage(normalImage, for: .normal)
}
}
}
#IBInspectable var myAnswerState: Int {
get {
return self.answerState.rawValue
}
set (value) {
if let t: AnswerState = AnswerState(rawValue: value) {
self.setAnswerState(t)
} else {
self.setAnswerState(.normal)
}
}
}
#IBInspectable var letter: String = "" {
didSet {
if !letter.isEmpty {
self.letterLabel.text = String(letter.first!) + ":"
}
}
}
#IBInspectable var answer: String = "" {
didSet {
self.answerLabel.text = answer
}
}
public let letterLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = UIColor.orange
label.font = UIFont(name: "Arial Rounded MT Bold", size: 24)
label.backgroundColor = UIColor.red.withAlphaComponent(0.25)
return label
}()
public let answerLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = UIColor.white
label.font = UIFont(name: "Arial Rounded MT Bold", size: 22)
label.backgroundColor = UIColor.green.withAlphaComponent(0.25)
return label
}()
override func layoutSubviews() {
super.layoutSubviews()
self.letterLabel.frame = CGRect(x: 40, y: 0, width: 30, height: self.frame.height)
self.answerLabel.frame = CGRect(x: 80, y: 0, width: 310, height: self.frame.height)
}
private func setAnswerState(_ state: AnswerState) {
self.answerState = state
}
private func setLetter(_ letter: String) {
self.letter = letter
}
private func setAnswer(_ answer: String) {
self.answer = answer
}
init(letter: String, selectionState: AnswerState, answer: String) {
super.init(frame: CGRect(x: 0, y: 0, width: 420, height: 47))
setAnswer(answer)
self.sharedInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.sharedInit()
}
#objc
func touchUpInside(sender: UIButton) {
self.setAnswerState(.answered)
}
func sharedInit() {
self.adjustsImageWhenHighlighted = false
self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
self.setLetter(letter)
self.setAnswer(answer)
// don't call this here!
//self.setAnswerState(.normal)
self.addSubview(self.letterLabel)
self.addSubview(self.answerLabel)
// load normal / answered images once
let dynamicBundle = Bundle(for: AnswerButton.self)
if let img = UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil) {
self.normalImage = img
}
if let img = UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil) {
self.answeredImage = img
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.sharedInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.sharedInit()
}
}
Related
I created a badge over bar button like this in order to show no of items in cart.
Class SSBadgeButton
class SSBadgeButton: UIButton {
var badgeLabel = UILabel()
var badge: String? {
didSet {
addBadgeToButon(badge: badge)
}
}
public var badgeBackgroundColor = UIColor.red {
didSet {
badgeLabel.backgroundColor = badgeBackgroundColor
}
}
public var badgeTextColor = UIColor.white {
didSet {
badgeLabel.textColor = badgeTextColor
}
}
public var badgeFont = UIFont.systemFont(ofSize: 12.0) {
didSet {
badgeLabel.font = badgeFont
}
}
public var badgeEdgeInsets: UIEdgeInsets? {
didSet {
addBadgeToButon(badge: badge)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addBadgeToButon(badge: nil)
}
func addBadgeToButon(badge: String?) {
badgeLabel.text = badge
badgeLabel.textColor = badgeTextColor
badgeLabel.backgroundColor = badgeBackgroundColor
badgeLabel.font = badgeFont
badgeLabel.sizeToFit()
badgeLabel.textAlignment = .center
let badgeSize = badgeLabel.frame.size
let height = max(18, Double(badgeSize.height) + 5.0)
let width = max(height, Double(badgeSize.width) + 10.0)
var vertical: Double?, horizontal: Double?
if let badgeInset = self.badgeEdgeInsets {
vertical = Double(badgeInset.top) - Double(badgeInset.bottom)
horizontal = Double(badgeInset.left) - Double(badgeInset.right)
let x = (Double(bounds.size.width) - 10 + horizontal!)
let y = -(Double(badgeSize.height) / 2) - 10 + vertical!
badgeLabel.frame = CGRect(x: x, y: y, width: width, height: height)
} else {
let x = self.frame.width - CGFloat((width / 2.0))
let y = CGFloat(-(height / 2.0))
badgeLabel.frame = CGRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height))
}
badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2
badgeLabel.layer.masksToBounds = true
addSubview(badgeLabel)
badgeLabel.isHidden = badge != nil ? false : true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addBadgeToButon(badge: nil)
fatalError("init(coder:) has not been implemented")
}
}
Inside View Controller :
func addBadge(itemvalue: String) {
let bagButton = SSBadgeButton()
bagButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
bagButton.tintColor = UIColor.darkGray
bagButton.setImage(UIImage(named: "ShoppingBag")?.withRenderingMode(.alwaysTemplate), for: .normal)
bagButton.badgeEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15)
bagButton.badge = itemvalue
bagButton.isUserInteractionEnabled = true
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: bagButton)
}
Using badge function :
self.addBadge(itemvalue: data[0]["total_products_in_cart"].stringValue)
But after adding this badge the bar button item becomes untapable. Control not going inside of the button action method. What should be done here in order to make button tapable.
Please check UITapGestureRecognizer on your parent view of the view controller. if exists removed the tap gesture, this make UIBarButtonItems began to respond properly to all selectors.
I was not able to judge whats the issue with the code of SSBadgeButton.
Later I figured out that what I want to achieve (placing UILabel with UIBarButtonItem) can be achieved through storyboard.
Solution is :
U can drag UIView inside ToolBar and can place UiLabel & Button inside of that view.
Referred from this answer
I have SegmentedControl with 2 lines using:
// AppDelegate
UILabel.appearanceWhenContainedInInstancesOfClasses([UISegmentedControl.self]).numberOfLines = 0
The problem is the line fonts are the same exact size. I need to change the titleTextAttributes for each line so that the second line is smaller then the first line.
I know I can use this for both lines:
segmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17))
How can I do this?
// The SegmentedControl
let segmentedControl: UISegmentedControl = {
let segmentedControl = UISegmentedControl(items: ["Pizza\n123.1K", "Turkey Burgers\n456.2M", "Gingerale\n789.3B"])
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.tintColor = UIColor.orange
segmentedControl.backgroundColor = .white
segmentedControl.isHighlighted = true
segmentedControl.addTarget(self, action: #selector(selectedIndex(_:)), for: .valueChanged)
return segmentedControl
}()
You'll want to create a custom control by subclassing UIControl. Here's a quick example:
CustomSegmentedControl.swift
import UIKit
import CoreImage
public class CustomSegmentedControl: UIControl {
public var borderWidth: CGFloat = 1.0
public var selectedSegementIndex = 0 {
didSet {
self.styleButtons()
}
}
public var numberOfSegments: Int {
return self.segments.count
}
private var buttons: [UIButton] = []
private var stackView = UIStackView(frame: CGRect.zero)
private var stackBackground = UIView(frame: CGRect.zero)
private var segments: [NSAttributedString] = [] {
didSet {
for subview in self.stackView.arrangedSubviews {
subview.removeFromSuperview()
}
self.buttons = []
for i in 0..<segments.count {
let segment = segments[i]
self.createAndAddSegmentButton(title: segment)
}
self.styleButtons()
}
}
override public init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
private func setup() {
self.addSubview(stackBackground)
self.stackBackground.constrainToBounds(of: self)
self.addSubview(stackView)
self.stackView.constrainToBounds(of: self)
self.stackView.axis = .horizontal
self.stackView.distribution = .fillEqually
self.stackView.spacing = borderWidth
self.layer.cornerRadius = 5.0
self.layer.borderWidth = borderWidth
self.clipsToBounds = true
self.stackBackground.backgroundColor = tintColor
}
private func createAndAddSegmentButton(title: NSAttributedString) {
let button = createSegmentButton(title: title)
self.buttons.append(button)
self.stackView.addArrangedSubview(button)
}
private func createSegmentButton(title: NSAttributedString) -> UIButton {
let button = UIButton(frame: CGRect.zero)
button.titleLabel?.numberOfLines = 0
button.titleLabel?.textAlignment = .center
button.setAttributedTitle(title, for: .normal)
button.addTarget(self, action: #selector(self.actSelected(button:)), for: .touchUpInside)
return button
}
override public var tintColor: UIColor! {
willSet {
self.layer.borderColor = newValue.cgColor
self.stackBackground.backgroundColor = newValue
}
}
public func setSegments(_ segments: [NSAttributedString]) {
self.segments = segments
}
#objc private func actSelected(button: UIButton) {
guard let index = self.buttons.index(of: button) else {
print("invalid selection should never happen, would want to handle better than this")
return
}
self.selectedSegementIndex = index
self.sendActions(for: .valueChanged)
}
private func styleButtons() {
for i in 0..<self.buttons.count {
let button = self.buttons[i]
if i == selectedSegementIndex {
button.backgroundColor = self.tintColor
button.titleLabel?.textColor = self.backgroundColor ?? .white
} else {
button.backgroundColor = self.backgroundColor
button.titleLabel?.textColor = self.tintColor
}
}
}
}
extension UIView {
func constrainToBounds(of view: UIView) {
self.translatesAutoresizingMaskIntoConstraints = false
let attrs: [NSLayoutAttribute] = [.leading, .top, .trailing, .bottom]
let constraints = attrs.map { (attr) -> NSLayoutConstraint in
return NSLayoutConstraint(item: self,
attribute: attr,
relatedBy: .equal,
toItem: view,
attribute: attr,
multiplier: 1.0,
constant: 0)
}
NSLayoutConstraint.activate(constraints)
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var customSegment: CustomSegmentedControl!
private var segments: [NSAttributedString] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.customSegment.backgroundColor = .white
self.customSegment.tintColor = .orange
let pizza = createText(title: "Pizza", subTitle: "123K")
let turkey = createText(title: "Turkey Burgers", subTitle: "456.2M")
let gingerAle = createText(title: "Gingerale", subTitle: "789.3B")
self.segments = [pizza, turkey, gingerAle]
self.customSegment.setSegments(self.segments)
self.customSegment.addTarget(self, action: #selector(self.segmentSelectionChanged(control:)), for: .valueChanged)
}
#objc private func segmentSelectionChanged(control: CustomSegmentedControl) {
let segment = self.segments[control.selectedSegementIndex]
print("selected segment = \(segment.string)")
}
func createText(title: String, subTitle: String) -> NSAttributedString {
let titleStr = NSMutableAttributedString(string: "\(title)\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16)])
let subStr = NSAttributedString(string: subTitle, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 10)])
titleStr.append(subStr)
return titleStr
}
}
I know this question has been asked lots of times but none of solutions worked for me.
I have a custom UIView class which I use for displaying alert message. I added UIButton to close the view. However, nothing happens when I tab it.
import UIKit
public class Alert: UIView {
public var image: UIImage?
public var title: String?
public var message: String?
public var closeButtonText: String?
public var dialogBackgroundColor: UIColor = .white
public var dialogTitleTextColor: UIColor = .black
public var dialogMessageTextColor: UIColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
public var dialogImageColor: UIColor = UIColor(red:0.47, green:0.72, blue:0.35, alpha:1.0)
public var overlayColor: UIColor = .black
public var overlayOpacity: CGFloat = 0.66
public var paddingSingleTextOnly: CGFloat = 8
public var paddingTopAndBottom: CGFloat = 24
public var paddingFromSides: CGFloat = 8
public var seperatorHeight: CGFloat = 6
private var height: CGFloat = 0
private var width: CGFloat = 0
private var maxSize: CGSize = CGSize()
private let marginFromSides: CGFloat = 80
public lazy var imageSize: CGSize = CGSize(width: 75, height: 75)
public var overlay = false
public var blurOverlay = true
//animation duration
public var duration = 0.33
private var onComplete: (() -> Void)?
#objc public var titleFont: UIFont = UIFont.systemFont(ofSize: 18)
#objc public var messageFont: UIFont = UIFont.systemFont(ofSize: 15)
private lazy var backgroundView: UIView = {
let view = UIView()
view.alpha = 0
return view
}()
public let dialogView: UIView = {
let view = UIView()
view.layer.cornerRadius = 6
view.layer.masksToBounds = true
view.alpha = 0
view.clipsToBounds = true
return view
}()
private lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
return view
}()
public lazy var closeButton: UIButton = {
let button = UIButton()
return button
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
#objc func closeButtonTapped(sender: UIButton){
dismiss()
}
private func calculations() {
height += paddingTopAndBottom
maxSize = CGSize(width: frame.width - marginFromSides * 2, height: frame.height - marginFromSides)
}
public convenience init(title:String, message: String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
self.image = image
}
public convenience init(title:String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.image = image
}
public convenience init(title: String, message: String) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
}
public convenience init(message: String) {
self.init(frame: UIScreen.main.bounds)
paddingTopAndBottom = paddingSingleTextOnly
paddingFromSides = paddingSingleTextOnly * 2
self.message = message
}
override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createOverlay() {
backgroundView.frame = frame
backgroundView.backgroundColor = overlayColor
backgroundView.isUserInteractionEnabled = true
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createBlurOverlay() {
backgroundView.frame = frame
//Blur Effect
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = frame
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundView.addSubview(blurEffectView)
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createTitle(title: String) {
titleLabel.font = titleFont
titleLabel.text = title
titleLabel.frame.origin.y = height + 2
let titleLabelSize = titleLabel.sizeThatFits(maxSize)
handleSize(size: titleLabelSize)
titleLabel.frame.size = titleLabelSize
titleLabel.textColor = self.dialogTitleTextColor
dialogView.addSubview(titleLabel)
}
private func createMessage(message: String) {
messageLabel.font = messageFont
messageLabel.text = message
messageLabel.frame.origin.y = height
let messageLabelSize = messageLabel.sizeThatFits(maxSize)
messageLabel.frame.size = messageLabelSize
messageLabel.textColor = self.dialogMessageTextColor
handleSize(size: messageLabelSize)
dialogView.addSubview(messageLabel)
}
private func createImage(image: UIImage) {
imageView.image = image.withRenderingMode(.alwaysTemplate)
imageView.frame.origin.y = height
imageView.frame.size = imageSize
imageView.tintColor = self.dialogImageColor
handleSize(size: imageSize)
dialogView.addSubview(imageView)
}
private func createButton(){
closeButton.setTitle("Close", for: .normal)
closeButton.tintColor = UIColor.white
closeButton.frame.origin.y = height + 20
let closeButtonSize = CGSize(width: width - 60, height: 60)
closeButton.frame.size = closeButtonSize
closeButton.layer.cornerRadius = 6
closeButton.backgroundColor = Color.NavigationBar.tintColor
closeButton.isUserInteractionEnabled = true
handleSize(size: closeButtonSize)
dialogView.addSubview(closeButton)
}
private func createDialog() {
centerAll()
height += paddingTopAndBottom
dialogView.frame.size = CGSize(width: width, height: height)
dialogView.backgroundColor = self.dialogBackgroundColor
dialogView.isUserInteractionEnabled = true
addSubview(dialogView)
self.dialogView.center = self.center
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
if let window = UIApplication.shared.keyWindow {
window.addSubview(dialogView)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
UIApplication.topViewController()?.view.addSubview(self)
window.view.addSubview(self)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
}
}
private func centerAll() {
if ((messageLabel.text) != nil) {
messageLabel.frame.origin.x = (width - messageLabel.frame.width) / 2
}
if ((titleLabel.text) != nil) {
titleLabel.frame.origin.x = (width - titleLabel.frame.width) / 2
}
if ((imageView.image) != nil) {
imageView.frame.origin.x = (width - imageView.frame.width) / 2
}
closeButton.frame.origin.x = (width - closeButton.frame.width) / 2
}
private func handleSize(size: CGSize) {
if width < size.width + paddingFromSides * 2 {
width = size.width + paddingFromSides * 2
}
if paddingTopAndBottom != paddingSingleTextOnly {
height += seperatorHeight
}
height += size.height
}
private func showAnimation() {
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = self.overlayOpacity
self.dialogView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
self.dialogView.alpha = 1
})
}
public func show() {
if let complete = onComplete {
self.onComplete = complete
}
calculations()
if self.overlay {
if blurOverlay {
createBlurOverlay()
} else {
createOverlay()
}
}
if let img = image {
createImage(image: img)
}
if let title = title {
createTitle(title: title)
}
if let message = message {
createMessage(message: message)
}
createButton()
createDialog()
showAnimation()
}
public func dismiss(){
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = 0
}
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
self.dialogView.alpha = 0
}, completion: { (completed) in
self.dialogView.removeFromSuperview()
if (self.overlay)
{
self.backgroundView.removeFromSuperview()
}
self.removeFromSuperview()
if let completionHandler = self.onComplete {
completionHandler()
}
})
}
}
How I create the alert;
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
If I declare target inside UIViewController (Where I create this UIView) as
Alert.closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:), for: .touchUPInside)
and create function inside UIViewController It is working. I can't figure out why It doesn't work when in custom class.
So my question is that how can close the alert view when tabbed the button?
I tried below solution but didn't work for me;
UIButton target action inside custom class
Assuming these lines are inside a function - such as due to a button tap:
#IBAction func didTap(_ sender: Any) {
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
}
You are creating an instance of your Alert class, calling the .show() function inside it, and then it goes out of scope.
So, as soon as that function exists, alert no longer exists, and no code inside it can run.
You need to have a class-level variable to hold onto it while it is displayed:
class MyViewController: UIViewController {
var alert: Alert?
#IBAction func didTap(_ sender: Any) {
alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert?.show()
}
}
Here is a demonstration of the "Wrong Way" and the "Right Way" to handle your Alert view: https://github.com/DonMag/EmreTest
Look this code
1- Call IBAction inside UIView Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
self.view.addSubview(alert)
}
}
2- Call IBAction inside UIViewController Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
// closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
alert.closeButton.addTarget(self, action: #selector(self.closeButtonTappedController(_:)), for: .touchUpInside)
self.view.addSubview(alert)
}
#IBAction func closeButtonTappedController(_ sender:UIButton){
print("Call 2")
}
}
I am a beginner I want to make a top bar button that has badge like the picture above, after searching on the internet, I can make the badge on the button by implementing the SSBadgeButton like the code below
import UIKit
class SSBadgeButton: UIButton {
var badgeLabel = UILabel()
var badge: String? {
didSet {
addBadgeToButon(badge: badge)
}
}
public var badgeBackgroundColor = UIColor.red {
didSet {
badgeLabel.backgroundColor = badgeBackgroundColor
}
}
public var badgeTextColor = UIColor.white {
didSet {
badgeLabel.textColor = badgeTextColor
}
}
public var badgeFont = UIFont.systemFont(ofSize: 12.0) {
didSet {
badgeLabel.font = badgeFont
}
}
public var badgeEdgeInsets: UIEdgeInsets? {
didSet {
addBadgeToButon(badge: badge)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addBadgeToButon(badge: nil)
}
func addBadgeToButon(badge: String?) {
badgeLabel.text = badge
badgeLabel.textColor = badgeTextColor
badgeLabel.backgroundColor = badgeBackgroundColor
badgeLabel.font = badgeFont
badgeLabel.sizeToFit()
badgeLabel.textAlignment = .center
let badgeSize = badgeLabel.frame.size
let height = max(18, Double(badgeSize.height) + 5.0)
let width = max(height, Double(badgeSize.width) + 10.0)
var vertical: Double?, horizontal: Double?
if let badgeInset = self.badgeEdgeInsets {
vertical = Double(badgeInset.top) - Double(badgeInset.bottom)
horizontal = Double(badgeInset.left) - Double(badgeInset.right)
let x = (Double(bounds.size.width) - 10 + horizontal!)
let y = -(Double(badgeSize.height) / 2) - 10 + vertical!
badgeLabel.frame = CGRect(x: x, y: y, width: width, height: height)
} else {
let x = self.frame.width - CGFloat((width / 2.0))
let y = CGFloat(-(height / 2.0))
badgeLabel.frame = CGRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height))
}
badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2
badgeLabel.layer.masksToBounds = true
addSubview(badgeLabel)
badgeLabel.isHidden = badge != nil ? false : true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addBadgeToButon(badge: nil)
fatalError("init(coder:) has not been implemented")
}
}
as we can see the SSBadgeButtonis UIButton, and I need to convert that SSBadgeButton to UIBarButtonItem. the purpose of this is to make the UIBarButtonItem class to be accessible in the Interface builder as the custom class like the picture below
You don't need to convert the UIButton to UIBarButtonItem, you can always create UIBarbuttonItem using UIButton as shown below
let button = UIButton()
button.setTitle("ABCD", for: .normal)
let uiBarButtonItem = UIBarButtonItem(customView: button)
self.navigationItem.leftBarButtonItems = [uiBarButtonItem]
Instead of UIButton you will use your SSBadgeButton thats all
Hope it helps
you can create UIBarButtonItem with custom button
let button = SSBadgeButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30)
let barBtnItem = UIBarButtonItem(customView: button)
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)