Prog created Buttons from class don't appear - Swift IOS - swift

I'm trying to add a number of customClass buttons to my view automatically, based on the size of an array.
Created the class and called the appropriate method in the class, but nothing shows up. Debugging tells me that the method is called/executed as expected (3x).
When I add the function directly to the view controller it does work.
What am I missing here ???
ViewController code:
import UIKit
class ViewController: UIViewController {
let userArray: [String] = ["One","Two","Three"]
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
CustomCheckBox().showNewButton()
}
}
.. Other stuff...
}
CustomButton class code:
{
import UIKit
class CustomCheckBox: UIButton {
let checkedImage: UIImage = UIImage(named:"chckbox_on")!
let uncheckedImage: UIImage = UIImage(named: "chckbox_off")!
var newButton: CustomCheckBox!
..... other functions (isChecked, buttonClicked, ..)
func showNewButton (){
newButton = CustomCheckBox (type: UIButtonType.Custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.isChecked=false
self.addSubview(newButton)
}
}

May consider restructuring your code like this
class CustomCheckBoxContainer: UIView {
var newButton: CustomCheckBox!
func showNewButton (){
newButton = CustomCheckBox (type: UIButtonType.custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), for: UIControlEvents.TouchUpInside)
newButton.isChecked=false
self.addSubview(newButton)
}
}
class CustomCheckBox: UIButton {
let checkedImage: UIImage = UIImage(named:"chckbox_on")!
let uncheckedImage: UIImage = UIImage(named: "chckbox_off")!
//add here all your button functionality
}
and then change your view did load to something like this:
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
let customCheckBoxContainer = CustomCheckBoxContainer()
customCheckBoxContainer.showNewButton()
self.view.addSubview(customCheckBoxContainer)
}
}

pass your current View as parameter of your function like this.
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
CustomCheckBox().showNewButton(self.view)
}
}
Modify your custom UIButton function newButton like this
func showNewButton (currentView : UIView){
newButton = CustomCheckBox (type: UIButtonType.Custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.isChecked=false
currentView.addSubview(newButton)
}

Related

Button action selector inside class

I recently started developing in Swift (normally embedded C developer).
I want to create some button (later more than one) programmatically and change its label (just for practice).
For this I created a button class, which contains the button init and the callback function. My problem is that it seems like the #selector is not pointing to the instance of the button class the way I expected it will, so a button click does nothing. Can you tell me what I am doing wrong?
#objc class buttontest : NSObject{
let button = NSButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
#objc func printSomething() {
print("Hello")
self.button.title="TEST13"
}
func buttoninit() -> NSButton{
self.button.title="Test"
self.button.bezelStyle=NSButton.BezelStyle.rounded
self.button.target=self;
//button.action = Selector(ViewController.printSomething)
self.button.action = #selector(self.printSomething)
return self.button
}
}
class ViewController: NSViewController {
private lazy var redBox = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(redBox)
redBox.wantsLayer = true
redBox.layer?.backgroundColor = NSColor.red.cgColor
//button.init("Test12",self,Selector(printSomething())
let button = buttontest()
self.view.addSubview(button.buttoninit())
//self.view.addSubview(buttontest().buttoninit())
// Do any additional setup after loading the view.
}
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: NSScreen.main?.frame.width ?? 100, height: NSScreen.main?.frame.height ?? 100))
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
The OK version:
#objc class buttontest : NSObject{
let button = NSButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
#objc func printSomething() {
print("Hello")
self.button.title="TEST13"
}
func buttoninit() -> NSButton{
self.button.title="Test"
self.button.bezelStyle=NSButton.BezelStyle.rounded
self.button.target=buttonX
self.button.action = #selector(buttontest.printSomething)
return self.button
}
}
let buttonX = buttontest()
class ViewController: NSViewController {
private lazy var redBox = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(redBox)
redBox.wantsLayer = true
redBox.layer?.backgroundColor = NSColor.red.cgColor
self.view.addSubview(buttonX.buttoninit())
}
}

create a if statement where only one button at a time can have a border

I want my swift code to use a if statement or another sequence to only display a border on one of the buttons if click at a time. So a border can only be seen on one button at a time that button would be the last one pressed. I know I could say layer.border with 0 on each button that should be selected but I want to see if there is a more efficient way to do this.
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
ba.addTarget(self, action: #selector(pressa), for: .touchDown)
bb.addTarget(self, action: #selector(pressb), for: .touchDown)
bc.addTarget(self, action: #selector(pressc), for: .touchDown)
}
#objc func pressa(){
ba.layer.borderWidth = 2
}
#objc func pressb(){
bb.layer.borderWidth = 2
}
#objc func pressc(){
bc.layer.borderWidth = 2
}
}
you can add target to all buttons at the forEach and be only one method as #Sh_Khan mention
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}
}
See how you have used an array in [ba,bb,bc].forEach { ... } to reduce code duplication? Using arrays is the key. Rather than putting the three buttons in an array inline like that, create a property instead:
var buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
buttons = [ba,bb,bc]
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(buttonPressed), for: .touchDown)
}
...
}
I have used the same selector buttonPressed for all three buttons. buttonPressed can accept a parameter of type UIButton, that tells us which button is pressed:
#objc func buttonPressed(_ sender: UIButton) {
buttons.forEach { ba.layer.borderWidth = 0 } // deselect all buttons first...
sender.layer.borderWidth = 2 // select the tapped button
}
If you have more than 3 buttons to manage, I suggest you don't use UIButtons at all. You should use a UICollectionView. (Learn how to use them) This view will handle the selection for you. It also allows scrolling when there's not enough space to show all the buttons. You just need to create a custom UICollectionViewCell and override its isSelected property:
override var isSelected: Bool {
didSet {
if isSelected {
self.layer.borderWidth = 2
} else {
self.layer.borderWidth = 0
}
}
}
It could be 1 method like this
[ba,bb,bc].forEach { $0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown) }
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}

How to add another button to a button?

Everything works fine, but when I try to call the createSecondContainer () method from configureAddButton() it doesn't work. That is, pressing the button itself does not work.
override func viewDidLoad() {
super.viewDidLoad()
configureMainContainer()
}
func configureMainContainer(){
view.addSubview(mainContainer)
let frame = CGRect(x: view.bounds.midX - 60, y: view.bounds.midY, width: 120, height: 40)
mainContainer.frame = frame
mainContainer.addTarget(self, action: #selector(showAlert), for: .touchUpInside)
configureAddButton()
}
func configureAddButton(){
mainContainer.addSubview(addButton)
let frame = CGRect(x: mainContainer.bounds.maxX + 5, y: mainContainer.bounds.midY / 2, width: 20, height: 20)
addButton.frame = frame
addButton.addTarget(self, action: #selector(createSecondContainer), for: .touchUpInside)
}
#objc func createSecondContainer(){
print("something to complete")
}
You can use this Code working fine
import UIKit
class ViewController: UIViewController {
let mainContainer = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
configureMainContainer()
}
func configureMainContainer(){
let frame = CGRect(x: view.bounds.midX - 60, y: view.bounds.midY, width: 120, height: 40)
mainContainer.frame = frame
mainContainer.addTarget(self, action: #selector(showAlert), for: .touchUpInside)
mainContainer.backgroundColor = UIColor.red
self.view.addSubview(mainContainer)
configureAddButton()
}
#objc func showAlert() {
// Your Alert Code here
}
func configureAddButton(){
let addButton = UIButton()
let frame = CGRect(x: mainContainer.bounds.midX, y: mainContainer.bounds.midY / 2, width: 20, height: 20)
addButton.frame = frame
addButton.addTarget(self, action: #selector(createSecondContainer), for: .touchUpInside)
addButton.backgroundColor = UIColor.blue
mainContainer.addSubview(addButton)
}
#objc func createSecondContainer(){
print("something to complete")
}
}

Swift access helper function view

I try to access the function "addNavBar()" from, my helper class, but when I run the emulator, no view is shown on HomeViewController.
Swift 4
HomeViewController.swift
class HomeController: UIViewController {
let NavBar = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
NavBar.addNavBar()
}
}
NavigationBarHelper.swift
class NavigationBarHelper: UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
self in NavigationBarHelper is not the same object as the view in the view controller. Pass the VC's view as a parameter. There is no need to make NavigationBarHelper a subclass of UIView (in fact it could also be a struct).
class NavigationBarHelper {
func addNavBar(to view: UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
please also stick to naming conventions
class HomeController: UIViewController {
let navBarHelper = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
navBarHelper.addNavBar(to: self.view)
}
Instead of creating an object every usage
let NavBar = NavigationBarHelper()
I think it's more robust to make it static like this
class NavigationBarHelper: UIView {
static func addNavBar(view:UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
and call it directly without object creation
NavigationBarHelper.addNavBar(view: self.view)
A Better Alternative for this would be an extension so you don't have to create the special class for this
extension UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
And in you UIViewController you can simply write this without creating an object of your helper.
self.view.addNavBar()

Present viewController after touchUp GIDSignInButton()!

I make a Google Auth for my App, but I don't know how to present a new viewController after touchup GIDSignInButton!
Here how I make GIDSignInButton:
viewDidLoad (){
let googleBtn = GIDSignInButton()
googleBtn.frame = CGRect(x: 16, y: 500 + 66, width: view.frame.width - 32, height: 35)
view.addSubview(googleBtn)}
Here's a code example that will present a second, programmatically generated view controller using a standard UIButton. Obviously, you could do the same with your GIDSignInButton:
class MyViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
let button = UIButton(frame: CGRect(x: 10, y: 250, width: self.view.frame.width - 20, height: 35))
button.setTitle("Go to VC2", for: .normal)
button.backgroundColor = UIColor.blue
button.addTarget(self, action:#selector(self.buttonClicked), for: .touchUpInside)
self.view.addSubview(button)
}
func buttonClicked(sender: UIButton!)
{
let secondViewController = MySecondViewController()
present(secondViewController, animated: true, completion: {})
}
}
class MySecondViewController:UIViewController
{
override func viewDidLoad() {
self.view.backgroundColor = UIColor.darkGray
}
}
Note, however, that if you are presenting multiple views, you are advised to embed them in a Navigation Controller, as per Apple's Documentation