Swift - Dynamic number of buttons in tableviewcell - ios

Actually i have a project in github. The problem is that i cannot get why when scrolling, the buttons constraints in the cells are going crazy..
I didn't saw any project like this, then i have a reason to share it but i want to give a good example for another people.
I'll be very thankful with any help that drives me to the solution of this problem.
Best regards.
There it is the code for the cell:
import UIKit
class BookTableViewCell: UITableViewCell {
let nameLabel = UILabel()
let detailLabel = UILabel()
var cellButton = UIButton()
var cellLabel = UILabel()
var book : Book!
// MARK: Initalizers
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
func configureCellCon(botones:Int, titulo:String, book:Book) {
self.book = book
let marginGuide = contentView.layoutMarginsGuide
// configure titleLabel
contentView.addSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
nameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
nameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
nameLabel.numberOfLines = 0
nameLabel.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
nameLabel.text = book.name
// configure authorLabel
contentView.addSubview(detailLabel)
detailLabel.translatesAutoresizingMaskIntoConstraints = false
detailLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
detailLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
detailLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = true
detailLabel.numberOfLines = 0
detailLabel.font = UIFont(name: "Avenir-Book", size: 12)
detailLabel.textColor = UIColor.lightGray
detailLabel.text = book.details
var lastButton = UIButton()
if(book.buttonsAttibutes.count == 1) {
let button = UIButton()
button.tag = 0
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[0].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
return
}else if(book.buttonsAttibutes.count == 0) {
detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
return
}
if(book.buttonsAttibutes.count > 0) {
for x in 0...(book.buttonsAttibutes.count - 1) {
let button = UIButton()
button.tag = x
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[x].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
// button.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// button.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
if(x == 0){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
}else if(x == (book.buttonsAttibutes.count - 1)){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
}else{
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
}
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
lastButton = button
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func mostrarMensaje(sender: UIButton){
let message = self.book.buttonsAttibutes[sender.tag].message
let alertController = UIAlertController(title: "\(self.book.name)", message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Cerrar", style: UIAlertActionStyle.default) {
(result : UIAlertAction) -> Void in
}
alertController.addAction(okAction)
if let myViewController = parentViewController {
print(myViewController.title ?? "ViewController without title.")
myViewController.present(alertController, animated: true, completion: nil)
}
}
}

Ok, i went for the "InterfaceBuilder" and did it properly.
However, I'll be reading more about programmatic methods to do it.
thanks to all of you for the guide.
When creating the buttons and constraints programmatically, they are generating more constraints over the same cells making them crash.

Related

context over screen while transitioning between view controllers

When its clicked on some cell, DetailViewController will be opened. Problem is that context from DetailViewController is shown while transitioning between controllers.
Picture below presents problem:
image
DetailViewController is called in function:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let singleMovie = movieList[indexPath.row]
getSingleMovie(movieId: singleMovie.id, completion: {})
getDirector(movieId: singleMovie.id, completion: { [weak self] in
guard let self = self else { return }
var directorName = ""
self.creditResponse?.crew.forEach({ singleCredit in
if singleCredit.knownForDepartment == .directing {
directorName = singleCredit.name
}
})
guard let safeMovie = self.detailMovie else {return}
let detailVc = DetailViewController(movie: safeMovie, groups: self.checkGroups(groups: singleMovie.genreIds), director: directorName, movieIndex: indexPath.row)
self.navigationController?.pushViewController(detailVc, animated: true)
})
}
DetailViewController:
import UIKit
class DetailViewController: UIViewController {
let movie: Details?
let groupsValue: String?
let directorValue: String?
let movieIndex: Int?
var checkButton: Bool = false
var favoriteButton: Bool = false
let backButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "Icon"), for: .normal)
button.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
return button
}()
var movieTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Bold", size: 40)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
var groupsLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var directorLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Bold", size: 20)
label.textColor = .white
label.text = "Director: "
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var directorNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var descriptionLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
var movieImageView: UIImageView = {
let iv = UIImageView()
iv.layer.cornerRadius = 15
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let buttonChecked: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.imageView?.contentMode = .scaleAspectFit
button.imageEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.addTarget(self, action: #selector(checkButtonTapped), for: .touchUpInside)
return button
}()
let buttonFavorite: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.imageView?.contentMode = .scaleAspectFit
button.imageEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.addTarget(self, action: #selector(favoriteButtonTapped), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
self.navigationItem.hidesBackButton = true
setupUI()
}
init(movie:Details, groups: String, director: String, movieIndex: Int) {
self.movie = movie
self.groupsValue = groups
self.directorValue = director
self.movieIndex = movieIndex
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension DetailViewController{
#objc func backButtonTapped() {
navigationController?.popToRootViewController(animated: true)
}
}
extension DetailViewController {
func setupUI(){
view.addSubview(movieTitleLabel)
view.addSubview(groupsLabel)
view.addSubview(directorLabel)
view.addSubview(descriptionLabel)
view.addSubview(directorNameLabel)
view.addSubview(movieImageView)
view.addSubview(backButton)
view.addSubview(buttonChecked)
view.addSubview(buttonFavorite)
setupValues()
setupConstraints()
setupButtons()
}
}
extension DetailViewController {
func setupConstraints(){
let bottomImageConstraint = movieImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
bottomImageConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
movieImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
movieImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
movieImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bottomImageConstraint,
movieImageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3),
movieTitleLabel.topAnchor.constraint(equalTo: movieImageView.bottomAnchor, constant: 10),
movieTitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
movieTitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
groupsLabel.topAnchor.constraint(equalTo: movieTitleLabel.bottomAnchor, constant: 10),
groupsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorNameLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorNameLabel.leadingAnchor.constraint(equalTo: directorLabel.trailingAnchor, constant: 10),
descriptionLabel.topAnchor.constraint(equalTo: directorLabel.bottomAnchor, constant: 10),
descriptionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
backButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
backButton.heightAnchor.constraint(equalToConstant: 40),
backButton.widthAnchor.constraint(equalToConstant: 40),
buttonFavorite.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
buttonFavorite.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
buttonChecked.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
buttonChecked.trailingAnchor.constraint(equalTo: buttonFavorite.trailingAnchor, constant: -50),
])
}
func setupValues(){
movieTitleLabel.text = movie?.title
guard let safeUrl = movie?.posterPath else {return}
movieImageView.downloadImage(from: safeUrl)
descriptionLabel.text = movie?.overview
groupsLabel.text = groupsValue
directorNameLabel.text = directorValue
}
func setupButtons(){
let checkValue = UserDefaults.standard.bool(forKey: "checkButton\(movieIndex ?? 0)")
let favoriteValue = UserDefaults.standard.bool(forKey: "favoriteButton\(movieIndex ?? 0)")
if checkValue == true {
buttonChecked.setImage(UIImage(systemName: "checkmark.seal.fill"), for: .normal)
}
else {
buttonChecked.setImage(UIImage(systemName: "checkmark.seal"), for: .normal)
}
if favoriteValue == true {
buttonFavorite.setImage(UIImage(systemName: "star.fill"), for: .normal)
}
else {
buttonFavorite.setImage(UIImage(systemName: "star"), for: .normal)
}
}
}
extension DetailViewController{
#objc func checkButtonTapped() {
checkButton = !checkButton
if checkButton == true {
DispatchQueue.main.async {
self.buttonChecked.setImage(UIImage(systemName: "checkmark.seal.fill"), for: .normal)
}
}
else {
DispatchQueue.main.async {
self.buttonChecked.setImage(UIImage(systemName: "checkmark.seal"), for: .normal)
}
}
UserDefaults.standard.set(checkButton, forKey: "checkButton\(movieIndex ?? 0)")
}
#objc func favoriteButtonTapped() {
favoriteButton = !favoriteButton
if favoriteButton == true {
DispatchQueue.main.async {
self.buttonFavorite.setImage(UIImage(systemName: "star.fill"), for: .normal)
}
}
else {
DispatchQueue.main.async {
self.buttonFavorite.setImage(UIImage(systemName: "star"), for: .normal)
}
}
UserDefaults.standard.set(favoriteButton, forKey: "favoriteButton\(movieIndex ?? 0)")
}
}
How to setup animations so it transits normally?
Transition GIF
It looks like your DetailViewController.view has no backgroundColor set (OR it is .clear).
Please assign a backgroundColor to your view like this.
override func viewDidLoad() {
super.viewDidLoad()
// For testing, you may want to set this to something else like `.red` / `.yellow`
// This will help you in identifying where the issue is
self.view.backgroundColor = .black
}

Why is button background changing to blue when button selected?

I have three buttons, when selected they change background color to red - yet a blue background is popping up as well and I can't figure out why (sorry if the reason is obvious, ive only been doing "programming" for 3 weeks. I can't find anything in the code that would explain itI have three buttons, when selected they change background color to red - yet a blue background is popping up as well and I can't figure out why (sorry if the reason is obvious, ive only been doing "programming" for 3 weeks. I can't find anything in the code that would explain it
[enter image description here][1]import UIKit
class ViewController: UIViewController {
var counterEN : Int = 0
var count = Array(1...150).filter { $0 % 1 != 0}.count
var pizzaCount = 0
#IBOutlet weak var pizzaCounter: UILabel!
#IBOutlet weak var textField: UITextField!
#IBAction func test(_ sender: UIButton) {
self.textField.text = sender.currentTitle
}
var lastY: CGFloat = 100
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonClicked(_ sender: UIButton) {
pizzaCount = pizzaCount + 1
pizzaCounter.text = ("Antal pizza: ") + "\(pizzaCount)"
let contentView = UIView()
addViewsTo(contentView, title: sender.currentTitle)
contentView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentView)
// Add size constraints to the content view (260, 30)
NSLayoutConstraint(item: contentView, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 850.0).isActive = true
NSLayoutConstraint(item: contentView, attribute: .height, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: ` 30.0).isActive = true
// Add position constraints to the content view (horizontal center, 100 from the top)
NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: lastY).isActive = true
NSLayoutConstraint(item: contentView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0).isActive = true
// Update last Y position to have the gaps between views to be 10
lastY += 50
}
// Add label and button to the content view
func addViewsTo(_ contentView: UIView, title: String?) {
// Add a label with size of (100, 30)
let label = UILabel()
label.text = title
//label.text = ("1 x 17 ")
label.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 30.0)
contentView.addSubview(label)
let DressingButton = UIButton(type: .system)
DressingButton.frame = CGRect(x: 210, y: 0, width: 40, height: 30)
DressingButton.setTitle("D", for: .normal)
DressingButton.setTitleColor(.black, for: .normal)
DressingButton.setTitleColor(.red, for: .selected)
DressingButton.sendActions(for: .touchUpInside)
contentView.addSubview(DressingButton)
DressingButton.addTarget(self, action: #selector(dressingAction(_:)), for .touchUpInside)
let Chili = UIButton(type: .system)
Chili.frame = CGRect(x: 160, y: 0, width: 40, height: 30)
Chili.setTitle("C", for: .normal)
Chili.setTitleColor(.black, for: .normal)
Chili.backgroundColor = UIColor.white
Chili.setTitleColor(.systemRed, for: .selected)
Chili.sendActions(for: .touchUpInside)
contentView.addSubview(Chili)
Chili.addTarget(self, action: #selector(dressingAction(_:)), for: .touchUpInside)
let Hvidløg = UIButton(type: .system)
Hvidløg.frame = CGRect(x: 110, y: 0, width: 40, height: 30)
Hvidløg.setTitle("H", for: .normal)
Hvidløg.setTitleColor(.black, for: .normal)
Hvidløg.setTitleColor(.red, for: .selected)
Hvidløg.backgroundColor = UIColor.white
Hvidløg.sendActions(for: .touchUpInside)
contentView.addSubview(Hvidløg)
Hvidløg.addTarget(self, action: #selector(dressingAction(_:)), for: .touchUpInside)
let button2 = UIButton(type: .system)
button2.frame = CGRect(x: -40, y: 20, width: 150, height: 30)
button2.setTitle("Alm", for: .normal)
button2.setTitle("Ful", for: .selected)
button2.setTitle("Glu", for: .selected)
// Set button action
button2.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
contentView.addSubview(button2)
self.view = view
}
#objc func dressingAction(_ sender:UIButton){
sender.isSelected.toggle()
print("knap")
sender.backgroundColor = UIColor.red
}
#objc func buttonAction(_ sender:UIButton!)
{
sender.isSelected = !sender.isSelected
sender.setTitle("Alm", for: .normal)
if sender.isSelected{
sender.setTitle("Fuld", for: .selected)}
else {
sender.setTitle("Glu", for: .selected)
sender.isSelected = !sender.isSelected
// sender.tintColor = .clear
//// if sender.isSelected{
// sender.setTitleColor(.green, for: .selected)
// }
// else{
// sender.setTitleColor(.blue, for: .selected)
//
func buttonAction(_ button3:UIButton!) {
print("Button tapped"); counterEN += 1; count += 1; print (counterEN); print (count)
}
}
}
}
Please change your button type to .custom and the issue will be solved.
let dressingButton = UIButton(type: .custom)
Try changing your button Type to custom

correct layer swift programmatically

As you can see from the picture, despite the "masksToBounds" I see the layer under the botton x. How can I solve?
class StickerView: UIImageView {
let stickerIdentifier: String
let xButton = UIButton().then {
$0.setImage(Asset.delete_sticker.image.withRenderingMode(.alwaysOriginal), for: .normal)
$0.layer.cornerRadius = 30/2
$0.imageView?.contentMode = .scaleAspectFit
$0.layer.masksToBounds = true
$0.alpha = 1
$0.translatesAutoresizingMaskIntoConstraints = true
}
init(frame: CGRect, name: String) {
self.stickerIdentifier = "\(name.replacingOccurrences(of: ".png", with: ""))"
super.init(frame: frame)
setLayout()
}
func setLayout() {
self.addSubview(xButton)
self.bringSubviewToFront(xButton)
self.xButton.activate([
self.xButton.topAnchor.constraint(equalTo: self.topAnchor, constant: -10),
self.xButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 10),
self.xButton.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.15),
self.xButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.15),
])
}

Custom View size in iOS not dynamic

I just have started my first app on iOS a week ago. I have created a custom view to use it in my app using AppleDeveloperWebsite Custom Rating Control Tutorial.
Now I have chosen iPhone7 device in storyboard and I run this on iPhone 7 emulator it works perfectly but when I run it on iPhone 5 emulator (size of screen changes) my custom views extend beyond the screen. My all other controls sizes resize as I set constraints but only my custom view sizes get messed up.
Please Help
import UIKit
#IBDesignable class xRadioButtonView: UIView {
var button: UIButton!
var label: UILabel!
#IBInspectable var text: String? {
didSet{
label.text = text
}
}
//Properties
var isSelected = 0
//Initialization
override init(frame: CGRect){
super.init(frame: frame)
addSubviews()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
addSubviews()
}
func addSubviews() {
self.backgroundColor = UIColor.white
let xWidth = bounds.size.width
let xHeight = bounds.size.height
let tap = UITapGestureRecognizer(target: self, action: #selector(xRadioButtonView.radioButtonTextTapped))
button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
button.backgroundColor = UIColor.white
button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)
addSubview(button)
label = UILabel(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 2))
label.textColor = UIColor.init(hex: "#D5D5D5")
//label.font = UIFont.init(name: label.font.fontName, size: 25)
//label.font = label.font.withSize(25)
label.font = UIFont.boldSystemFont(ofSize: 25)
label.textAlignment = NSTextAlignment.center
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
addSubview(label)
}
override func layoutSubviews() {
// Set the button's width and height to a square the size of the frame's height.
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
label.text = "xRBV"
}
func radioButtonTapped(button: UIButton) {
if isSelected == 0 {
isSelected = 1
self.backgroundColor = UIColor.init(hex: "#00BFA5")
label.textColor = UIColor.init(hex: "#00BFA5")
} else {
isSelected = 0
self.backgroundColor = UIColor.white
label.textColor = UIColor.init(hex: "#D5D5D5")
}
}
func radioButtonTextTapped(sender: UITapGestureRecognizer){
if isSelected == 0 {
isSelected = 1
self.backgroundColor = UIColor.init(hex: "#00BFA5")
label.textColor = UIColor.init(hex: "#00BFA5")
} else {
isSelected = 0
self.backgroundColor = UIColor.white
label.textColor = UIColor.init(hex: "#D5D5D5")
}
}
}
As you can see PG button should finish where green color finishes but white color button is extended beyond the screen
You either need to set the frame in layoutSubViews or you need to implement autolayout in code:
button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
button.backgroundColor = UIColor.white
button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)
addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let attributes: [NSLayoutAttribute] = [.top, .bottom, .leading, .trailing]
let constants = [2, 2, 10, 10] // 2 from top and bottom, 10 from leading and trailing
NSLayoutConstraint.activate(attributes.enumerated().map { NSLayoutConstraint(item: button, attribute: $1, relatedBy: .equal, toItem: button.superview, attribute: $1, multiplier: 1, constant: constants[$0]) })
The example uses the old way because your constraints are uniform, but if you have something more complicated its often simpler to use NSLayoutAnchor as of iOS 9.
EDIT: here is the code for tuples if anyone is interested:
button.translatesAutoresizingMaskIntoConstraints = false
let attributes: [(NSLayoutAttribute, CGFloat)] = [(.top, 2), (.bottom, 2), (.leading, 12), (.trailing, 12)]
NSLayoutConstraint.activate(attributes.map { NSLayoutConstraint(item: button, attribute: $0.0, relatedBy: .equal, toItem: button.superview, attribute: $0.0, multiplier: 1, constant: $0.1) })
Thanks I wasn't even aware of this NSLayout yet. (HEHE 7 Days) Thanks to you I have a solution for my problem. Although I wanted different values for .top .bottom .leading .trailling
I used your code like this
NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: button.superview, attribute: .top, multiplier: 1, constant: 1).isActive = true
NSLayoutConstraint(item: button, attribute: .bottom, relatedBy: .equal, toItem: button.superview, attribute: .bottom, multiplier: 1, constant: 4).isActive = true
to all 4 sides. But is there a way to provide constant values as well like you have provided multiple attribute values?

Equal spacing between UIButtons using visual format language

I have a bunch of UIButtons that I want to space out evenly in a container view, right now I have this constraint for spacing:
someView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(>=0)-[M]-(>=0)-[T]-(>=0)-[W]-(>=0)-[T]-(>=0)-[F]-(>=0)-[S]-(>=0)-[S]-(>=0)-|", options: NSLayoutFormatOptions.AlignAllCenterY, metrics: nil, views: buttonsArray))
However this makes the buttons look like this:
The problem is what I want for spacing is calculated in this way:
spacing = (someView.frame.width - (someView.frame.height * 0.6) * 7) / 8
someView.frame.height * 0.6 is the side length of the buttons. I am not sure what to do.
Here is a simple code which does exactly distribute the spaces between buttons in a view, I hope this will help you figure out for your use case,
let containerView = UIView(frame: CGRect.zero)
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
let M = UIButton(type: .System)
M.translatesAutoresizingMaskIntoConstraints = false
M.backgroundColor = UIColor.lightGrayColor()
M.setTitle("M", forState: .Normal)
containerView.addSubview(M)
let T = UIButton(type: .System)
T.translatesAutoresizingMaskIntoConstraints = false
T.setTitle("T", forState: .Normal)
T.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(T)
let W = UIButton(type: .System)
W.translatesAutoresizingMaskIntoConstraints = false
W.setTitle("W", forState: .Normal)
W.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(W)
let Th = UIButton(type: .System)
Th.translatesAutoresizingMaskIntoConstraints = false
Th.setTitle("T", forState: .Normal)
Th.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(Th)
let F = UIButton(type: .System)
F.translatesAutoresizingMaskIntoConstraints = false
F.setTitle("F", forState: .Normal)
F.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(F)
let S = UIButton(type: .System)
S.translatesAutoresizingMaskIntoConstraints = false
S.setTitle("S", forState: .Normal)
S.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(S)
let Su = UIButton(type: .System)
Su.translatesAutoresizingMaskIntoConstraints = false
Su.setTitle("Su", forState: .Normal)
Su.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(Su)
let views = [
"M": M,
"T": T,
"W": W,
"Th":Th,
"F": F,
"S": S,
"Su": Su
]
let horizontalSpacing = 20
let cornerMargin = 30
let metrics = [
"horizontalSpacing": horizontalSpacing,
"cornerMargin": cornerMargin
]
views.values.forEach { view in
view.clipsToBounds = true
view.layer.cornerRadius = 10
}
let verticalCenter = NSLayoutConstraint(item: containerView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0)
let horizontalCenter = NSLayoutConstraint(item: containerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0)
view.addConstraint(verticalCenter)
view.addConstraint(horizontalCenter)
let horizontalFormat = "H:|-(==cornerMargin)-[M]-horizontalSpacing-[T]-horizontalSpacing-[W]-horizontalSpacing-[Th]-horizontalSpacing-[F]-horizontalSpacing-[S]-horizontalSpacing-[Su]-(==cornerMargin)-|"
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(horizontalFormat, options: .AlignAllCenterY, metrics: metrics, views: views)
view.addConstraints(horizontalConstraints)
let verticalFormat = "V:|-[M]-|"
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(verticalFormat, options: .AlignAllCenterY, metrics: metrics, views: views)
view.addConstraints(verticalConstraints)
And, here is the result,

Resources