PlaceHolder animates when start typing in TextField in iOS - ios

How to set this type of animation in UITextField? Nowadays, Many apps are using this.

I've found the solution. You can manage this type of animation using multiple labels, and show-hide those labels into textFieldDidBeginEditing method.
If you want nice animation same as you describe into your question, then try once following third party repository for UITextField.
JVFloatLabeledTextField
UIFloatLabelTextField
FloatLabelFields
If you are looking for the UITextView equivalent of this animation, please visit UIFloatLabelTextView repository.

This problem can be solved logically with the use of multiple labels and text-fields and later we can add animation if needed. I will like to explain this problem using three images, namely Img1, Img2 and Img3.
Img1 points to storyboard, where we have designed our interface. Here we have used three Labels each followed by TextField and UIView(line below Textfield).
Img2: It points to the initial screen when the app launches or when we press the "Sign up" Button at the bottom, which resets the screen. In this Image, the labels are hidden as textfields are blank with and view color is gray.
Img3: This image reflects the editing of Textfield. As we start editing text field(here the first one, namely name), the label shows up, size of textfield decreases, placeholder changes and color of view changes to black.
We need to keep one more thing in mind, when we stop editing any textfield and if it is still blank then set it properties to original.
I am adding code for this Question which I was asked as assignment in an interview.
import UIKit
class FloatingLabelViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
//UITextFieldDelegate - protocol defines methods that you use to manage the editing and validation of text in a UITextField object. All of the methods of this protocol are optional.
//UINavigationControllerDelegate - Use a navigation controller delegate (a custom object that implements this protocol) to modify behavior when a view controller is pushed or popped from the navigation stack of a UINavigationController object.
#IBOutlet weak var NameLbl: UILabel!
#IBOutlet weak var EmailLbl: UILabel!
#IBOutlet weak var PasswordLbl: UILabel!
#IBOutlet weak var NameTxf: UITextField!
#IBOutlet weak var EmailTxf: UITextField!
#IBOutlet weak var PasswordTxf: UITextField!
#IBOutlet weak var SignUpBtn: UIButton!
#IBOutlet weak var NameView: UIView!
#IBOutlet weak var EmailView: UIView!
#IBOutlet weak var PasswordView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NameTxf.delegate = self
EmailTxf.delegate = self
PasswordTxf.delegate = self
self.property()
//black is varaiable here
//setTitleColor - Sets the color of the title to use for the specified state
//var layer - The view’s Core Animation layer used for rendering. this propert is never nil
//cg color - Quartz color refernce
SignUpBtn.backgroundColor = UIColor.black
SignUpBtn.setTitleColor(UIColor.white, for: .normal)
SignUpBtn.layer.borderWidth = 1
SignUpBtn.layer.borderColor = UIColor.black.cgColor
//Tap guesture recognizer to hide keyboard
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(FloatingLabelViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
// UITapGestureRecognizer - UITapGestureRecognizer is a concrete subclass of UIGestureRecognizer that looks for single or multiple taps. For the gesture to be recognized, the specified number of fingers must tap the view a specified number of times.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//textFieldShouldReturn - Asks the delegate if the text field should process the pressing of the return button. The text field calls this method whenever the user taps the return button. YES if the text field should implement its default behavior for the return button; otherwise, NO.
// endEditing - Causes the view (or one of its embedded text fields) to resign the first responder status.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
//When user Starts Editing the textfield
// textFieldDidBeginEditing - Tells the delegate that editing began in the specified text field
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.NameTxf
{
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.NameLbl.isHidden = false
self.NameLbl.text = self.NameTxf.placeholder
self.NameTxf.placeholder = "First Last"
NameView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
else if textField == self.EmailTxf
{
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.EmailLbl.isHidden = false
self.EmailLbl.text = self.EmailTxf.placeholder
self.EmailTxf.placeholder = "abc#gmail.com"
EmailView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
else if textField == self.PasswordTxf
{
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 15)
self.PasswordLbl.isHidden = false
self.PasswordLbl.text = self.PasswordTxf.placeholder
self.PasswordTxf.placeholder = "........."
PasswordView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
}
//When User End editing the textfield.
// textFieldDidEndEditing - Tells the delegate that editing stopped for the specified text field.
func textFieldDidEndEditing(_ textField: UITextField) {
//Checkes if textfield is empty or not after after user ends editing.
if textField == self.NameTxf
{
if self.NameTxf.text == ""
{
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.NameLbl.isHidden = true
self.NameTxf.placeholder = "Name"
NameView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
else if textField == self.EmailTxf
{
if self.EmailTxf.text == ""
{
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.EmailLbl.isHidden = true
self.EmailTxf.placeholder = "Email"
EmailView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
else if textField == self.PasswordTxf
{
if self.PasswordTxf.text == ""
{
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.PasswordLbl.isHidden = true
self.PasswordTxf.placeholder = "Password"
PasswordView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}
}
//Action on SingUp button Clicked.
#IBAction func signupClicked(_ sender: Any) {
self.property()
self.dismissKeyboard() //TO dismiss the Keyboard on the click of SIGNUP button.
}
//Function to set the property of Textfields, Views corresponding to TextFields and Labels.
func property(){
NameLbl.isHidden = true
EmailLbl.isHidden = true
PasswordLbl.isHidden = true
NameTxf.text = ""
EmailTxf.text = ""
PasswordTxf.text = ""
NameTxf.placeholder = "Name"
EmailTxf.placeholder = "Email"
PasswordTxf.placeholder = "Password"
self.NameTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.EmailTxf.font = UIFont.italicSystemFont(ofSize: 25)
self.PasswordTxf.font = UIFont.italicSystemFont(ofSize: 25)
EmailTxf.keyboardType = UIKeyboardType.emailAddress
PasswordTxf.isSecureTextEntry = true
NameTxf.autocorrectionType = .no
EmailTxf.autocorrectionType = .no
NameView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
EmailView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
PasswordView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
}

For Swift 4.0 and 4.2
Check this library for floating textField
https://github.com/hasnine/iOSUtilitiesSource
Code:
enum placeholderDirection: String {
case placeholderUp = "up"
case placeholderDown = "down"
}
public class IuFloatingTextFiledPlaceHolder: UITextField {
var enableMaterialPlaceHolder : Bool = true
var placeholderAttributes = NSDictionary()
var lblPlaceHolder = UILabel()
var defaultFont = UIFont()
var difference: CGFloat = 22.0
var directionMaterial = placeholderDirection.placeholderUp
var isUnderLineAvailabe : Bool = true
override init(frame: CGRect) {
super.init(frame: frame)
Initialize ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Initialize ()
}
func Initialize(){
self.clipsToBounds = false
self.addTarget(self, action: #selector(IuFloatingTextFiledPlaceHolder.textFieldDidChange), for: .editingChanged)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: true)
if isUnderLineAvailabe {
let underLine = UIImageView()
underLine.backgroundColor = UIColor.init(red: 197/255.0, green: 197/255.0, blue: 197/255.0, alpha: 0.8)
// underLine.frame = CGRectMake(0, self.frame.size.height-1, self.frame.size.width, 1)
underLine.frame = CGRect(x: 0, y: self.frame.size.height-1, width : self.frame.size.width, height : 1)
underLine.clipsToBounds = true
self.addSubview(underLine)
}
defaultFont = self.font!
}
#IBInspectable var placeHolderColor: UIColor? = UIColor.lightGray {
didSet {
self.attributedPlaceholder = NSAttributedString(string: self.placeholder! as String ,
attributes:[NSAttributedString.Key.foregroundColor: placeHolderColor!])
}
}
override public var placeholder:String? {
didSet {
// NSLog("placeholder = \(placeholder)")
}
willSet {
let atts = [NSAttributedString.Key.foregroundColor.rawValue: UIColor.lightGray, NSAttributedString.Key.font: UIFont.labelFontSize] as! [NSAttributedString.Key : Any]
self.attributedPlaceholder = NSAttributedString(string: newValue!, attributes:atts)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: self.enableMaterialPlaceHolder)
}
}
override public var attributedText:NSAttributedString? {
didSet {
// NSLog("text = \(text)")
}
willSet {
if (self.placeholder != nil) && (self.text != "")
{
let string = NSString(string : self.placeholder!)
self.placeholderText(string)
}
}
}
#objc func textFieldDidChange(){
if self.enableMaterialPlaceHolder {
if (self.text == nil) || (self.text?.count)! > 0 {
self.lblPlaceHolder.alpha = 1
self.attributedPlaceholder = nil
self.lblPlaceHolder.textColor = self.placeHolderColor
self.lblPlaceHolder.frame.origin.x = 0 ////\\
let fontSize = self.font!.pointSize;
self.lblPlaceHolder.font = UIFont.init(name: (self.font?.fontName)!, size: fontSize-3)
}
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {() -> Void in
if (self.text == nil) || (self.text?.count)! <= 0 {
self.lblPlaceHolder.font = self.defaultFont
self.lblPlaceHolder.frame = CGRect(x: self.lblPlaceHolder.frame.origin.x+10, y : 0, width :self.frame.size.width, height : self.frame.size.height)
}
else {
if self.directionMaterial == placeholderDirection.placeholderUp {
self.lblPlaceHolder.frame = CGRect(x : self.lblPlaceHolder.frame.origin.x, y : -self.difference, width : self.frame.size.width, height : self.frame.size.height)
}else{
self.lblPlaceHolder.frame = CGRect(x : self.lblPlaceHolder.frame.origin.x, y : self.difference, width : self.frame.size.width, height : self.frame.size.height)
}
}
}, completion: {(finished: Bool) -> Void in
})
}
}
func EnableMaterialPlaceHolder(enableMaterialPlaceHolder: Bool){
self.enableMaterialPlaceHolder = enableMaterialPlaceHolder
self.lblPlaceHolder = UILabel()
self.lblPlaceHolder.frame = CGRect(x: 0, y : 0, width : 0, height :self.frame.size.height)
self.lblPlaceHolder.font = UIFont.systemFont(ofSize: 10)
self.lblPlaceHolder.alpha = 0
self.lblPlaceHolder.clipsToBounds = true
self.addSubview(self.lblPlaceHolder)
self.lblPlaceHolder.attributedText = self.attributedPlaceholder
//self.lblPlaceHolder.sizeToFit()
}
func placeholderText(_ placeholder: NSString){
let atts = [NSAttributedString.Key.foregroundColor: UIColor.lightGray, NSAttributedString.Key.font: UIFont.labelFontSize] as [NSAttributedString.Key : Any]
self.attributedPlaceholder = NSAttributedString(string: placeholder as String , attributes:atts)
self.EnableMaterialPlaceHolder(enableMaterialPlaceHolder: self.enableMaterialPlaceHolder)
}
override public func becomeFirstResponder()->(Bool){
let returnValue = super.becomeFirstResponder()
return returnValue
}
override public func resignFirstResponder()->(Bool){
let returnValue = super.resignFirstResponder()
return returnValue
}
override public func layoutSubviews() {
super.layoutSubviews()
}
}

You can try using JSInputField which supports data validations as well.
JSInputField *inputField = [[JSInputField alloc] initWithFrame:CGRectMake(10, 100, 300, 50)];
[self.view addSubview:inputField];
[inputField setPlaceholder:#"Enter Text"];
[inputField setRoundedCorners:UIRectCornerAllCorners];
[inputField addValidationRule:JSCreateRuleNotNullValue]; //This will validate field for null value. It will show error if field is empty.
[inputField addValidationRule:JSCreateRuleNumeric(2)]; //This will validate field for numeric values and restrict to enter value upto 2 decimal places.

You can use SkyFloatingLabelTextField. It is SkyScanner's library for this kind of label or textField animations.
https://github.com/Skyscanner/SkyFloatingLabelTextField
I hope this answer will works for you.
Enjoy.

use this code
[your_textfieldname setShowsTouchWhenHighlighted:YES];

Related

Swift - Why does my UIButton need to be double tapped to be in its selection state?

I have a UIButton, and the expected behavior is when it is tapped, it should be have the blue background color, and when untapped, it should be white. However, I always have to double tap it on the first try for it to become selected... why is that?
class CheckmarkCell: UITableViewCell {
static let reuseIdentifier = String(describing: CheckmarkCell.self)
#IBOutlet private weak var titleLabel: UILabel!
#IBOutlet private weak var yesButton: UIButton!
private weak var delegate: CheckmarkCellDelegate?
private var value: Bool?
public func configure(title: String, value: Bool?, delegate: CheckmarkCellDelegate? = nil) {
self.titleLabel.text = title
self.value = value
self.delegate = delegate
self.yesButton.layer.masksToBounds = true
self.yesButton.layer.cornerRadius = self.yesButton.frame.width / 2
self.yesButton.layer.borderWidth = 1.0
self.yesButton.layer.borderColor = UIColor.NBABlue?.cgColor
self.yesButton.backgroundColor = UIColor.white
// This is the dot
self.yesButton.tintColor = UIColor.clear
// This block of code saves the values. If it is removed, when you scroll, values will all be deselected and false.
switch self.value {
case true:
// This is the saved state
self.yesButton.backgroundColor = UIColor.NBABlue
self.yesButton.tintColor = UIColor.clear
// Deselected, should e white.
case false:
self.yesButton.backgroundColor = UIColor.white
self.yesButton.tintColor = UIColor.clear
default:
break
}
}
#IBAction func buttonTapped(_ sender: UIButton) {
if sender.isSelected {
yesButton.isSelected = false
// The selected state
self.yesButton.backgroundColor = UIColor.NBABlue
self.yesButton.tintColor = UIColor.clear
if self.value == nil || self.value == false {
self.value = true
self.delegate?.checkmarkCell(self, selectedValue: self.value!)
}
} else {
yesButton.isSelected = true
self.yesButton.backgroundColor = UIColor.white
self.yesButton.tintColor = UIColor.clear
if self.value == nil || self.value == true {
self.value = false
self.delegate?.checkmarkCell(self, selectedValue: self.value!)
}
}
}
}
For remove blue background you have to clear tint colour from storyboard.
In section file IBAction write this line.
sender.isSelected = !sender.isSelected

Add dynamic type to a UISearchBar

My question is pretty much stated in the title. I'm trying to add dynamic type (as defined here) to a UISearchBar with no luck. I know this is possible as the system apps seem to be able to handle it just fine as shown here:
However, my app doesn't seem to be handling that so well as shown here:
Knowing that UITextField is contained within UISearchBar I naturally tried this solution without success:
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).adjustsFontForContentSizeCategory = true
I've also tried searching online/checking documentation but I can't seem to find a solution anywhere. Is there something I'm missing to get dynamic type working in a UISearchBar.
Update:
#matt suggested I do a manual check and update the font that way. However, that is yielding another issue as the search bar itself is too small to fit the text as shown here:
#matt suggested to update the height as well using the scaledValue(for:) method, however this doesn't seem to work. Here's the code I'm using:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).font = UIFont.preferredFont(forTextStyle: .body)
let textFieldFrame = UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).frame
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).frame = CGRect(x: textFieldFrame.minX, y: textFieldFrame.minY, width: textFieldFrame.width, height: UIFontMetrics.default.scaledValue(for: textFieldFrame.height))
}
The font seems to now be scaling with this updated code, yet the search bar's height isn't growing:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
searchBar.textField?.font = UIFont.preferredFont(forTextStyle: .body)
if let textFieldFrame = searchBar.textField?.frame {
searchBar.textField?.frame = CGRect(x: textFieldFrame.minX, y: textFieldFrame.minY, width: textFieldFrame.width, height: UIFontMetrics.default.scaledValue(for: textFieldFrame.height))
}
}
Also, here's how I found the textField (just in case other users who get stuck would like to know):
extension UISearchBar {
var textField: UITextField? {
var _textField: UITextField? = nil
subviews.forEach {
$0.subviews.forEach {
if let textField = $0 as? UITextField {
_textField = textField
}
}
}
return _textField
}
}
I have followed these milestones to reach your goal:
Automatically Adjusts Font with the Dynamic Type feature (STEP 1).
Adapt the searchbar constraints (STEP 2) AND its textfield constraints (STEP 3) when a new preferred content size category occurs.
class SearchBarDynamicTypeVC: UIViewController {
#IBOutlet weak var mySearchBar: UISearchBar!
let fontHead = UIFont(name: "Chalkduster", size: 20.0)
let fontHeadMetrics = UIFontMetrics(forTextStyle: .title1)
var initialFrameHeight: CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).font = fontHeadMetrics.scaledFont(for: fontHead!)
mySearchBar.textField?.adjustsFontForContentSizeCategory = true //STEP 1
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
initialFrameHeight = mySearchBar.intrinsicContentSize.height
if let textField = mySearchBar.textField {
adaptConstraints(textField) //Initialization according to the first preferred content size category
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if let textField = mySearchBar.textField,
let _ = previousTraitCollection {
adaptConstraints(textField) // STEP 2 & 3
}
}
private func adaptConstraints(_ textField: UITextField) {
// Adapt the SEARCHBAR constraints
mySearchBar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.deactivate(mySearchBar.constraints)
let heightSB = mySearchBar.heightAnchor.constraint(equalToConstant: fontHeadMetrics.scaledValue(for: initialFrameHeight))
let widthSB = mySearchBar.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20)
let centerVSB = mySearchBar.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
let centerHSB = mySearchBar.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
NSLayoutConstraint.activate([centerVSB,
centerHSB,
widthSB,
heightSB])
// Adapt the SEARCHBAR TEXTFIELD constraints
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.deactivate(textField.constraints)
let centerXTF = textField.centerXAnchor.constraint(equalTo: textField.superview!.centerXAnchor)
let centerYTF = textField.centerYAnchor.constraint(equalTo: textField.superview!.centerYAnchor)
let widthTF = textField.widthAnchor.constraint(equalTo: textField.superview!.widthAnchor, constant: -20.0)
let heightTF = textField.heightAnchor.constraint(equalTo: textField.superview!.heightAnchor, constant: -20.0)
NSLayoutConstraint.activate([centerXTF,
centerYTF,
widthTF,
heightTF])
}
}
I used the code snippet provided in your post to get the searchbar textfield:
extension UISearchBar {
var textField: UITextField? {
var _textField: UITextField? = nil
subviews.forEach {
$0.subviews.forEach {
if let textField = $0 as? UITextField {
_textField = textField
}
}
}
return _textField
}
}
However, you can also get it using the key searchField as follows:
let textField = searchBar.value(forKey: "searchField") as? UITextField
The snapshots hereunder show the final result:
You can now add dynamic type to a UISearchBar by adapting the code snippet above and customizing the visual personal choices (text style, font, margins...).

UITextfield showing and dismissing very quickly in UIPageViewController (Swift)

I have a UIPageViewController as walkthrough presentation for my app. The walkthrough has basically 5 pages in total. Page 4 has a UITextField programmatically set. Don't mind the other objects like labels or Images.
The problem I have is that when the user reaches page 4, The user is presented with a Textfield which i'd like the keyboard to show up automatically via a becomeFirstResponder.
When I load this 4th view with the textfield, It shows up very quickly on the simulator and on my device and then goes back down. It basically shows up via the becomeFirstResponder code but somehow then resigns within 1 second.
I have tried this programmatically and also via the storyboard UITextfield to see if that made a difference but both seem to not work.
The code that executes a label is where I also executed the UITextField programmatically but for this example, I removed it and used an IBOutlet instead as you can see.
Also, in this case 3 I would put the becomeFirstResponder to activate the keyboard but it still did not work as expected.
Right now I left it in the viewWillAppear method as you can see.
Here is my code for this specific scenario:
import UIKit
class WalkthroughViewController: UIViewController {
#IBOutlet var headingLabel:UILabel!
#IBOutlet var contentLabel:UILabel!
#IBOutlet var contentImageView:UIImageView!
#IBOutlet var pageControl:UIPageControl!
#IBOutlet var forwardButton:UIButton!
// This was added via storyboard via drag and drop
#IBOutlet var nameField: UITextField!
// This is a UITextfield programmatically
let textField = UITextField(frame: CGRectMake(20, 200.0, 330.0, 40.0)) // x=x-cord, y=y-cord, Width, Height
// May 2 Updates
func textFieldFunc() {
textField.textAlignment = NSTextAlignment.Center
textField.textColor = UIColor.wetAsphaltColor()
textField.backgroundColor = UIColor.whiteColor()
textField.font = UIFont(name: "avenir", size: 21)
textField.borderStyle = UITextBorderStyle.None
textField.autocapitalizationType = UITextAutocapitalizationType.Words // If you need any capitalization
textField.becomeFirstResponder()
self.view.addSubview(textField)
}
func nameLabel() {
let label = UILabel(frame: CGRectMake(15, 180, 265, 33))
label.center = CGPointMake(185, 160)
label.textColor = UIColor.cloudsColor()
label.font = UIFont(name: "avenir", size: 30)
label.textAlignment = NSTextAlignment.Center
label.text = "Whats your name?"
self.view.addSubview(label)
}
#IBAction func submit(sender: AnyObject) {
// Thisis going to handle the name later and it will then reguster the user for later use within the app
}
var index = 0
var heading = ""
var imageFile = ""
var content = ""
override func viewDidLoad() {
super.viewDidLoad()
headingLabel.text = heading
contentLabel.text = content
contentImageView.image = UIImage(named: imageFile)
pageControl.currentPage = index
// 0...2
switch index {
case 0...2: forwardButton.setTitle("NEXT", forState: UIControlState.Normal)
nameField.hidden = true
// 3
case 3:
nameLabel()
nameField.hidden = false
case 4:
forwardButton.setTitle("DONE", forState: UIControlState.Normal)
default: break
}
print("Index: \(index)")
}
#IBAction func nextButtonTapped(sender: UIButton) {
switch index {
case 0...3: // 2
let pageViewController = parentViewController as! WalkthroughPageViewController
pageViewController.forward(index)
case 4: // 3
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(true, forKey: "finishedWalkedThrough")
dismissViewControllerAnimated(true, completion: nil)
default: break
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if nameField.hidden == false {
nameField.becomeFirstResponder()
}
}
}
My question is, how can I remedy this so that when the 4th page is presented in a slide through manner, the UITextfield can present the keyboard like a normal view?
As I saw in a very similar post, This actually worked. I managed to solve it by using this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if index == 3 {
dispatch_async(dispatch_get_main_queue(), {() -> Void in
let strongSelf: WalkthroughViewController = self
strongSelf.textField.becomeFirstResponder()
})
}
}
Now when I scroll to the 4th page (3rd from 0), It loads the keyboard with the .becomeFirstResponder as a normal UITextField would.
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^{
[self.yourTextField becomeFirstResponder];
});
For Swift 3:
DispatchQueue.main.async(execute: {() -> Void in
let strongSelf: WalkthroughViewController = self
strongSelf. textField.becomeFirstResponder()
})
I didn't have to specify the index position of the ViewController

UIButton not clickable when implemented from a class

I have a class ManageCell, which stores the frames, set the text of labels, etc... Which are the sub-views of an UIView CellView which is in the ViewController.
ManageCell:
import Foundation
import UIKit
class ManageCell {
var name: UILabel
var viewBelowButton: UIView
var deleteButton: UIButton
init (name: String) {
self.name = UILabel(frame: CGRectMake(10,15,250,40)
self.name.text = name
self.name.sizeToFit()
self.viewBelowButton = UIButton(frame: CGRectMake(UIScreen.mainScreen.bounds.width, 0, 70, 70)
//set outside the visible area so that it can be animated in.
self.viewBelowButton.backgroundColor = UIColor.redColor()
self.deleteButton = UIButton(frame: CGRectMake(0,0,70,70))
self.deleteButton.addTarget(ViewController.self, action: "deleteButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.deleteButton.setTitle("delete", forState: .Normal)
}
}
ViewController:
var cellView: [UIView] = []
var manageCells: [ManageCell] = []
...
//fill the manageCells array
func setSubViews () {
for (index, cell) in manageCells.enumerate() {
cellView.append(UIView(frame: CGRectMake(0, originY, view.bounds.width, 70)
cellView[index].addSubview(cell.name)
cellView[index].addSubview(cell.viewBelowButton)
cell.viewBelowButton.addSubview(cell.deleteButton)
}
}
func editing () {
var frame = CGRectMake(view.bound.width - 70, 0, 0, 70)
for cell in cells {
UIView.animateWithDuration(0.2, animations: {
cell.viewBelowButton.frame = frame
}
}
}
func deleteButtonPressed(sender: UIButton) {
print("button pressed")
}
User Interaction is enabled on both cellView[index], viewBelowButton and deleteButton.
The problem I'm facing is that the deleteButton does not respond to touches. The deleteButtonPressed: function is not being called.
code: https://github.com/an23lm/swift-stuff
I'm not sure if this is good practice, any suggestions are welcome.
Of course it's not called, ViewController.self is a class type, not your View Controller. And if even it was, it's not a good practice. You should use a delegate pattern here with some parameter to be returned back, so you will distinguish which cell delete button was pressed.
Example on your code:
protocol ManageCellDelegate: class {
func manageCellDeletePressed(id: Int)
}
class ManageCell {
var name: UILabel
var viewBelowButton: UIView
var deleteButton: UIButton
weak var delegate: ManageCellDelegate?
var id: Int
init (id: Int, name: String) {
self.id = id
self.name = UILabel(frame: CGRectMake(10,15,250,40))
self.name.text = name
self.name.sizeToFit()
self.viewBelowButton = UIButton(frame: CGRectMake(UIScreen.mainScreen().bounds.width, 0, 70, 70))
//set outside the visible area so that it can be animated in.
self.viewBelowButton.backgroundColor = UIColor.redColor()
self.deleteButton = UIButton(frame: CGRectMake(0,0,70,70))
self.deleteButton.addTarget(self, action: "deleteButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.deleteButton.setTitle("delete", forState: .Normal)
}
func deleteButtonPressed(sender: AnyObject) {
self.delegate?.manageCellDeletePressed(id)
}
}
class ViewController: UIViewController {
var cellView: [UIView] = []
var manageCells: [ManageCell] = []
func fullManageCells() {
for id in 0...15 {
let manageCell = ManageCell(id: id, name: "something")
manageCell.delegate = self
manageCells.append(manageCell)
}
}
}
extension ViewController: ManageCellDelegate {
func manageCellDeletePressed(id: Int) {
println("button with id \(id) pressed")
}
}

How to create radio buttons and checkbox in swift (iOS)?

I am developing an app that allows to do survey. My layout is generated from XML based questions.
I need to create radio buttons (single choice) and checkboxes (multiple answers). I did not find anything useful for swift.
Does anyone have an idea?
Checkbox
You can create your own CheckBox control extending UIButton with Swift:
import UIKit
class CheckBox: UIButton {
// Images
let checkedImage = UIImage(named: "ic_check_box")! as UIImage
let uncheckedImage = UIImage(named: "ic_check_box_outline_blank")! as UIImage
// Bool property
var isChecked: Bool = false {
didSet {
if isChecked == true {
self.setImage(checkedImage, for: UIControl.State.normal)
} else {
self.setImage(uncheckedImage, for: UIControl.State.normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action:#selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
self.isChecked = false
}
#objc func buttonClicked(sender: UIButton) {
if sender == self {
isChecked = !isChecked
}
}
}
And then add it to your views with Interface Builder:
Radio Buttons
Radio Buttons can be solved in a similar way.
For example, the classic gender selection Woman - Man:
import UIKit
class RadioButton: UIButton {
var alternateButton:Array<RadioButton>?
override func awakeFromNib() {
self.layer.cornerRadius = 5
self.layer.borderWidth = 2.0
self.layer.masksToBounds = true
}
func unselectAlternateButtons() {
if alternateButton != nil {
self.isSelected = true
for aButton:RadioButton in alternateButton! {
aButton.isSelected = false
}
} else {
toggleButton()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
unselectAlternateButtons()
super.touchesBegan(touches, with: event)
}
func toggleButton() {
self.isSelected = !isSelected
}
override var isSelected: Bool {
didSet {
if isSelected {
self.layer.borderColor = Color.turquoise.cgColor
} else {
self.layer.borderColor = Color.grey_99.cgColor
}
}
}
}
You can init your radio buttons like this:
override func awakeFromNib() {
self.view.layoutIfNeeded()
womanRadioButton.selected = true
manRadioButton.selected = false
}
override func viewDidLoad() {
womanRadioButton?.alternateButton = [manRadioButton!]
manRadioButton?.alternateButton = [womanRadioButton!]
}
For Radio Buttons and CheckBoxes there is nothing that comes built in.
You can implement Checkboxes easily yourself. You can set an uncheckedImage for your button for UIControlStateNormal and a checkedImage for your UIControlStateSelected. Now on tap, the button will change its image and alternate between checked and unchecked image.
To use radio buttons, you have to keep an Array for all the buttons that you want to behave as radio buttons. Whenever a button is pressed, you need to uncheck all other buttons in the array.
For radio buttons you can use SSRadioButtonsController
You can create a controller object and add buttons array to it like
var radioButtonController = SSRadioButtonsController()
radioButtonController.setButtonsArray([button1!,button2!,button3!])
The main principle is something like this here.
Swift 5, Checkbox with animation
NOTE:- if you want to remove the blue background while isSelected change the UIButton type from System to Custom
Check my Example for the Checkbox and Radio button
https://github.com/rashidlatif55/CheckBoxAndRadioButton
Create an outlet for the button
#IBOutlet weak var checkBoxOutlet:UIButton!{
didSet{
checkBoxOutlet.setImage(UIImage(named:"unchecked"), for: .normal)
checkBoxOutlet.setImage(UIImage(named:"checked"), for: .selected)
}
}
Create an extension of UIButton
extension UIButton {
//MARK:- Animate check mark
func checkboxAnimation(closure: #escaping () -> Void){
guard let image = self.imageView else {return}
UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveLinear, animations: {
image.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
}) { (success) in
UIView.animate(withDuration: 0.1, delay: 0, options: .curveLinear, animations: {
self.isSelected = !self.isSelected
//to-do
closure()
image.transform = .identity
}, completion: nil)
}
}
}
How to use
#IBAction func checkbox(_ sender: UIButton){
sender.checkboxAnimation {
print("I'm done")
//here you can also track the Checked, UnChecked state with sender.isSelected
print(sender.isSelected)
}
}
Check out DLRadioButton. You can add and customize radio buttons directly from the Interface Builder. Also works with Swift perfectly.
Update: version 1.3.2 added square buttons, also improved performance.
Update: version 1.4.4 added multiple selection option, can be used as checkbox as well.
Update: version 1.4.7 added RTL language support.
Solution for Radio Button in Swift 4.2 without using third-party libraries
Create RadioButtonController.swift file and place following code in it:
import UIKit
class RadioButtonController: NSObject {
var buttonsArray: [UIButton]! {
didSet {
for b in buttonsArray {
b.setImage(UIImage(named: "radio_off"), for: .normal)
b.setImage(UIImage(named: "radio_on"), for: .selected)
}
}
}
var selectedButton: UIButton?
var defaultButton: UIButton = UIButton() {
didSet {
buttonArrayUpdated(buttonSelected: self.defaultButton)
}
}
func buttonArrayUpdated(buttonSelected: UIButton) {
for b in buttonsArray {
if b == buttonSelected {
selectedButton = b
b.isSelected = true
} else {
b.isSelected = false
}
}
}
}
Use it as below in your view controller file:
import UIKit
class CheckoutVC: UIViewController {
#IBOutlet weak var btnPaytm: UIButton!
#IBOutlet weak var btnOnline: UIButton!
#IBOutlet weak var btnCOD: UIButton!
let radioController: RadioButtonController = RadioButtonController()
override func viewDidLoad() {
super.viewDidLoad()
radioController.buttonsArray = [btnPaytm,btnCOD,btnOnline]
radioController.defaultButton = btnPaytm
}
#IBAction func btnPaytmAction(_ sender: UIButton) {
radioController.buttonArrayUpdated(buttonSelected: sender)
}
#IBAction func btnOnlineAction(_ sender: UIButton) {
radioController.buttonArrayUpdated(buttonSelected: sender)
}
#IBAction func btnCodAction(_ sender: UIButton) {
radioController.buttonArrayUpdated(buttonSelected: sender)
}
}
Be sure to add radio_off and radio_on images in Assets.
Result:
There's a really great library out there you can use for this (you can actually use this in place of UISwitch): https://github.com/Boris-Em/BEMCheckBox
Setup is easy:
BEMCheckBox *myCheckBox = [[BEMCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[self.view addSubview:myCheckBox];
It provides for circle and square type checkboxes
And it also does animations:
shorter ios swift 4 version:
#IBAction func checkBoxBtnTapped(_ sender: UIButton) {
if checkBoxBtn.isSelected {
checkBoxBtn.setBackgroundImage(#imageLiteral(resourceName: "ic_signup_unchecked"), for: .normal)
} else {
checkBoxBtn.setBackgroundImage(#imageLiteral(resourceName: "ic_signup_checked"), for:.normal)
}
checkBoxBtn.isSelected = !checkBoxBtn.isSelected
}
A very simple checkbox control.
#IBAction func btn_box(sender: UIButton) {
if (btn_box.selected == true)
{
btn_box.setBackgroundImage(UIImage(named: "box"), forState: UIControlState.Normal)
btn_box.selected = false;
}
else
{
btn_box.setBackgroundImage(UIImage(named: "checkBox"), forState: UIControlState.Normal)
btn_box.selected = true;
}
}
For a checkbox, you don't need to subclass the UIButton. It already has the isSelected property to handle this.
checkbox = UIButton.init(type: .custom)
checkbox.setImage(UIImage.init(named: "iconCheckboxOutlined"), for: .normal)
checkbox.setImage(UIImage.init(named: "iconCheckboxFilled"), for: .selected)
checkbox.addTarget(self, action: #selector(self.toggleCheckboxSelection), for: .touchUpInside)
Then in the action method toggle it's isSelected state.
#objc func toggleCheckboxSelection() {
checkbox.isSelected = !checkbox.isSelected
}
Steps to Create Radio Button
BasicStep : take Two Button. set image for both like selected and unselected.
than add action to both button.
now start code
1)Create variable :
var btnTag : Int = 0
2)In ViewDidLoad Define :
btnTag = btnSelected.tag
3)Now In Selected Tap Action :
#IBAction func btnSelectedTapped(sender: AnyObject) {
btnTag = 1
if btnTag == 1 {
btnSelected.setImage(UIImage(named: "icon_radioSelected"), forState: .Normal)
btnUnSelected.setImage(UIImage(named: "icon_radioUnSelected"), forState: .Normal)
btnTag = 0
}
}
4)Do code for UnCheck Button
#IBAction func btnUnSelectedTapped(sender: AnyObject) {
btnTag = 1
if btnTag == 1 {
btnUnSelected.setImage(UIImage(named: "icon_radioSelected"), forState: .Normal)
btnSelected.setImage(UIImage(named: "icon_radioUnSelected"), forState: .Normal)
btnTag = 0
}
}
Radio Button is Ready for you
You can simply subclass UIButton and write your own drawing code to suit your needs. I implemented a radio button like that of android using the following code. It can be used in storyboard as well.See example in Github repo
import UIKit
#IBDesignable
class SPRadioButton: UIButton {
#IBInspectable
var gap:CGFloat = 8 {
didSet {
self.setNeedsDisplay()
}
}
#IBInspectable
var btnColor: UIColor = UIColor.green{
didSet{
self.setNeedsDisplay()
}
}
#IBInspectable
var isOn: Bool = true{
didSet{
self.setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
self.contentMode = .scaleAspectFill
drawCircles(rect: rect)
}
//MARK:- Draw inner and outer circles
func drawCircles(rect: CGRect){
var path = UIBezierPath()
path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: rect.width, height: rect.height))
let circleLayer = CAShapeLayer()
circleLayer.path = path.cgPath
circleLayer.lineWidth = 3
circleLayer.strokeColor = btnColor.cgColor
circleLayer.fillColor = UIColor.white.cgColor
layer.addSublayer(circleLayer)
if isOn {
let innerCircleLayer = CAShapeLayer()
let rectForInnerCircle = CGRect(x: gap, y: gap, width: rect.width - 2 * gap, height: rect.height - 2 * gap)
innerCircleLayer.path = UIBezierPath(ovalIn: rectForInnerCircle).cgPath
innerCircleLayer.fillColor = btnColor.cgColor
layer.addSublayer(innerCircleLayer)
}
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.nativeScale
}
/*
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isOn = !isOn
self.setNeedsDisplay()
}
*/
override func awakeFromNib() {
super.awakeFromNib()
addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
isOn = false
}
#objc func buttonClicked(sender: UIButton) {
if sender == self {
isOn = !isOn
setNeedsDisplay()
}
}
}
I made a super simple class to handle this in a Mac application I'm working on. Hopefully, this is helpful to someone
RadioButtonController Class:
class RadioButtonController: NSObject {
var buttonArray : [NSButton] = []
var currentleySelectedButton : NSButton?
var defaultButton : NSButton = NSButton() {
didSet {
buttonArrayUpdated(buttonSelected: self.defaultButton)
}
}
func buttonArrayUpdated(buttonSelected : NSButton) {
for button in buttonArray {
if button == buttonSelected {
currentleySelectedButton = button
button.state = .on
} else {
button.state = .off
}
}
}
}
Implementation in View Controller:
class OnboardingDefaultLaunchConfiguration: NSViewController {
let radioButtonController : RadioButtonController = RadioButtonController()
#IBOutlet weak var firstRadioButton: NSButton!
#IBOutlet weak var secondRadioButton: NSButton!
#IBAction func folderRadioButtonSelected(_ sender: Any) {
radioButtonController.buttonArrayUpdated(buttonSelected: folderGroupRadioButton)
}
#IBAction func fileListRadioButtonSelected(_ sender: Any) {
radioButtonController.buttonArrayUpdated(buttonSelected: fileListRadioButton)
}
override func viewDidLoad() {
super.viewDidLoad()
radioButtonController.buttonArray = [firstRadioButton, secondRadioButton]
radioButtonController.defaultButton = firstRadioButton
}
}
For checkboxes there is actually a built-in solution in the form of UITableViewCell accessories. You can set up your form as a UITableView in which each cell as a selectable option and use accessoryType to set a check mark for selected items.
Here is a pseudo-code example:
let items = [SelectableItem]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get the item for the current row
let item = self.items[indexPath.row]
// ...dequeue and set up the `cell` as you wish...
// Use accessoryType property to mark the row as checked or not...
cell.accessoryType = item.selected ? .checkmark : .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Unselect row
tableView.deselectRow(at: indexPath, animated: false)
// Toggle selection
let item = self.items[indexPath.row]
item.selected = !item.selected
tableView.reloadData()
}
Radio buttons however do require a custom implementation, see the other answers.
The decision of checking or unchecking the checkbox button is something out of the scope of the view. View itself should only take care of drawing the elements, not deciding about the internal state of that. My suggested implementation is as follows:
import UIKit
class Checkbox: UIButton {
let checkedImage = UIImage(named: "checked")
let uncheckedImage = UIImage(named: "uncheked")
var action: ((Bool) -> Void)? = nil
private(set) var isChecked: Bool = false {
didSet{
self.setImage(
self.isChecked ? self.checkedImage : self.uncheckedImage,
for: .normal
)
}
}
override func awakeFromNib() {
self.addTarget(
self,
action:#selector(buttonClicked(sender:)),
for: .touchUpInside
)
self.isChecked = false
}
#objc func buttonClicked(sender: UIButton) {
if sender == self {
self.action?(!self.isChecked)
}
}
func update(checked: Bool) {
self.isChecked = checked
}
}
It can be used with Interface Builder or programmatically. The usage of the view could be as the following example:
let checkbox_field = Checkbox(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
checkbox_field.action = { [weak checkbox_field] checked in
// any further checks and business logic could be done here
checkbox_field?.update(checked: checked)
}
I don't have enough reputation to comment, so I'll leave my version of Salil Dwahan's version here. Works for Swift 5, XCode 11.3.
First place your button on IB, select type "Custom" and create an outlet and an action with the Assistant Layout (Ctrl + Drag). Include the following code and it should end like this:
class YourViewController: UIViewController {
#IBOutlet weak var checkbox: UIButton!
#IBAction func checkboxTapped(_ sender: UIButton) {
checkbox.isSelected = !checkbox.isSelected
}
override func viewDidLoad() {
super.viewDidLoad()
checkbox.setImage(UIImage.init(named: "checkMark"), for: .selected)
}
}
Don't forget to add the image to Assets and change the name to match!
checkbox.isSelected is the way to check
Though some of the answers mention it rightly that we can use the Selected State to set an image for Selected state of the button, it won't work elegantly when the button has to have both image and text.
Like many, I ended by subclassing UIButton; however, added support for setting images from Interface Builder.
Below is my code:
import UIKit
class CustomCheckbox: UIButton {
#IBInspectable var defaultStateImage: UIImage? = nil {
didSet{
self.setNeedsDisplay()
}
}
#IBInspectable var selectedStateImage: UIImage? = nil {
didSet{
self.setNeedsDisplay()
}
}
#IBInspectable var gapPadding: CGFloat = 0 {
didSet{
self.setNeedsDisplay()
}
}
#IBInspectable var isChecked: Bool = false {
didSet{
self.setNeedsDisplay()
}
}
var defaultImageView: UIImageView? = nil
var selectedImageView: UIImageView? = nil
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
if(defaultStateImage != nil) {
defaultImageView = UIImageView(image: defaultStateImage)
defaultImageView?.translatesAutoresizingMaskIntoConstraints = false
addSubview(defaultImageView!)
let length = CGFloat(16)
titleEdgeInsets.left += length
NSLayoutConstraint.activate([
defaultImageView!.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: -gapPadding),
defaultImageView!.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
defaultImageView!.widthAnchor.constraint(equalToConstant: length),
defaultImageView!.heightAnchor.constraint(equalToConstant: length)
])
}
if(selectedStateImage != nil) {
selectedImageView = UIImageView(image: selectedStateImage)
selectedImageView!.translatesAutoresizingMaskIntoConstraints = false
addSubview(selectedImageView!)
let length = CGFloat(16)
titleEdgeInsets.left += length
NSLayoutConstraint.activate([
selectedImageView!.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: -gapPadding),
selectedImageView!.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
selectedImageView!.widthAnchor.constraint(equalToConstant: length),
selectedImageView!.heightAnchor.constraint(equalToConstant: length)
])
}
if defaultImageView != nil {
defaultImageView!.isHidden = isChecked
}
if selectedImageView != nil {
selectedImageView!.isHidden = !isChecked
}
self.addTarget(self, action: #selector(checkChanged(_:)), for: .touchUpInside)
}
#objc func checkChanged(_ btn : UIButton){
self.isChecked = !self.isChecked
if defaultImageView != nil {
defaultImageView!.isHidden = isChecked
}
if selectedImageView != nil {
selectedImageView!.isHidden = !isChecked
}
}
}
Create 2 buttons one as "YES" and another as "NO".
Create a BOOL property Ex: isNRICitizen = false
Give same button connection to both the buttons and set a tag
(Ex: Yes button - tag 10 and No button -tag 20)
#IBAction func btnAction(_ sender:UIButton) {
isNRICitizen = sender.tag == 10 ? true : false
isNRICitizen ? self.nriCitizenBtnYes.setImage(#imageLiteral(resourceName: "radioChecked"), for: .normal) : self.nriCitizenBtnYes.setImage(#imageLiteral(resourceName: "radioUnchecked"), for: .normal)
isNRICitizen ? self.nriCitizenBtnNo.setImage(#imageLiteral(resourceName: "radioUnchecked"), for: .normal) : self.nriCitizenBtnNo.setImage(#imageLiteral(resourceName: "radioChecked"), for: .normal)
}
Swift 5.0 Updated Simple RadioButton For Swift (No Library)
First set images to button One Checked and Second Unchecked.
Then Provide 2 Outlet Of RadioButton.
#IBOutlet weak var radioMale: UIButton!
#IBOutlet weak var radioFemale: UIButton!
Create IBAction With Both Button Action in One Method.
#IBAction func btnRadioTapped(_ sender: UIButton) {
radioMale.setImage(UIImage(named: "Unchecked"), for: .normal)
radioFemale.setImage(UIImage(named: "Unchecked"), for: .normal)
if sender.currentImage == UIImage(named: "Unchecked"){
sender.setImage(UIImage(named: "Checked"), for: .normal)
}else{
sender.setImage(UIImage(named: "Unchecked"), for: .normal)
}
}
Couldn't find an easy SwiftUI version in this thread so here is a quick component using SF symbols.
struct CheckBox: View {
private let checked = Image("checkmark.square.fill")
private let unChecked = Image("checkmark.square")
#State private var isChecked: Bool = false
var body: some View {
ZStack {
isChecked == false ? unChecked : checked
}.onTapGesture {
isChecked.toggle()
}
}
}
If you use Image when change state. Try this:
var nightButtonState: Bool = false {
didSet {
nightButtonState ? autoNightButton.setBackgroundImage(UIImage(named: "unchecked_icon"), for: .normal) : autoNightButton.setBackgroundImage(UIImage(named: "checked_icon"), for: .normal)
}
}
Button action:
#IBAction func autoNightButtonAction(_ sender: UIButton) {
self.nightButtonState.toggle()
}

Resources