UIButton not calling action function in swift - ios

I have a button created programmatically but for some strange reason when pressed it it doesn't activate the action. There are a few buttons, I've eliminated the other buttons form the code, here is the button that isn't working:
struct Buttons {
var hintButton: UIButton!
let hintBtn = UIButton(frame: CGRect(x: 10, y: 20, width: 70, height: 70))
init() {hintBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
hintBtn.tag = 2
hintBtn.setTitle("Hint", forState: .Normal)
hintBtn.setTitleColor(UIColor.whiteColor(), forState: .Normal)
hintButton = hintBtn
}
}
Here is the code in the viewController:
override func viewDidLoad() {
super.viewDidLoad()
AddAllGraphics()
ButtonActions()
self.buttons.hintBtn.enabled = true
}
func AddAllGraphics() {
self.view.addSubview(buttons.hintBtn)
}
func ButtonActions() {
buttons.hintBtn.addTarget(self, action: "giveHint:", forControlEvents: .TouchUpInside)
}
func giveHint(sender:UIButton) {
if(sender.tag == 2){
buttons.hintBtn.enabled = false
print("It Works")
}
}
I've tried putting a break point at the "giveHint" function but it is never called when the button is pressed. I've tried putting self.buttons.btn.userInteractionEnabled = true in the viewDidLoad but it still doesn't work. Any help will be greatly appreciated!

The code above is working for me, but I had to init the struct properly:
struct Buttons {
var hintButton: UIButton!
let hintBtn = UIButton(frame: CGRect(x: 10, y: 50, width: 70, height: 70))
init() {
hintBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
hintBtn.tag = 2
hintBtn.setTitle("Hint", forState: .Normal)
hintBtn.setTitleColor(UIColor.whiteColor(), forState: .Normal)
hintButton = hintBtn
}
}

Found out the issue. The button was behind another view that had user interaction disabled. Since the view was clear I thought I was pressing the button.

Related

How to make globe method for custom button action in swift

I have a sign in screen in which two fields are there password and email textfield.
I added both textfield with stackview. After adding textfields, I need to add button of password lock. After clicking on password lock button user can see the password. I took button from library and try to add on textfield but, due to stackview it always set as third element for stackview. So I am unable to add.
So I decided to add with extension of textfield. So i added button successfully in utility class,
but unable to add action with selector. It showing an error
Value of type 'UIViewController' has no member 'passwordAction'
My code for add button is
extension UITextField {
func passwordButton(vc:UIViewController){
let paddingView = UIView(frame: CGRect(x: 25, y: 25, width: 24, height: 17))
let passwordButton = UIButton(frame: CGRect(x: 0, y: 0, width: 24, height: 17))
passwordButton.setImage(UIImage(named: "show_password"), for: .normal)
passwordButton.addTarget(vc, action: #selector(vc.passwordAction), for: .touchUpInside)
paddingView.addSubview(passwordButton)
self.addSubview(paddingView)
}
}
class Loginviewcontroller{
passwordTextField.passwordButton(vc: self)
func passwordAction(){
print("action")
}
}
I am calling this method from login controller.
So I have two question:-
when two textfield are attached with stackview, we can not put button on textfield with storyboard?
How can I make globle method to add button and add action that can access in uiviewcontroller?
Error is self explanatory, you added the passwordAction method to Loginviewcontroller but in func passwordButton you take a UIViewController as an argument. As a result even when you pass instance of Loginviewcontroller to passwordButton function call, in the function scope vc is just another UIViewController and clearly all UIViewControllers does not have a method named passwordAction.
As you said you wanna make global method to add button and add action that can access in uiviewcontroller you can follow below approach
Step 1: Declare a protocol
#objc protocol SecureTextProtocol where Self: UIViewController {
func passwordAction()
}
Step 2: Update your passwordButton method to take SecureTextProtocol instead of plain UIViewController
extension UITextField {
func passwordButton(vc:SecureTextProtocol){
let paddingView = UIView(frame: CGRect(x: 25, y: 25, width: 24, height: 17))
let passwordButton = UIButton(frame: CGRect(x: 0, y: 0, width: 24, height: 17))
passwordButton.setImage(UIImage(named: "show_password"), for: .normal)
passwordButton.addTarget(vc, action: #selector(vc.passwordAction), for: .touchUpInside)
paddingView.addSubview(passwordButton)
self.addSubview(paddingView)
}
}
Step 3: Make your ViewController's that want to get the button action to confirm to SecureTextProtocol protocol
class Loginviewcontroller: UIViewController, SecureTextProtocol {
passwordTextField.passwordButton(vc: self)
func passwordAction(){
print("action")
}
}
That should do the job
You can always solve this problem with simple code, that's the way I do it.
extension UITextField {
func setPasswordToggleImage(_ button: UIButton) {
if(isSecureTextEntry){
button.setImage(UIImage(named: "ic_password_visible"), for: .normal)
}else{
button.setImage(UIImage(named: "ic_password_invisible"), for: .normal)
}
}
func enablePasswordToggle(){
let button = UIButton(type: .custom)
setPasswordToggleImage(button)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -16, bottom: 0, right: 0)
button.frame = CGRect(x: CGFloat(self.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
button.addTarget(self, action: #selector(self.togglePasswordView), for: .touchUpInside)
self.rightView = button
self.rightViewMode = .always
}
#objc func togglePasswordView(_ sender: Any) {
self.isSecureTextEntry = !self.isSecureTextEntry
setPasswordToggleImage(sender as! UIButton)
}
}
then on viewcontroller's viewdidload just call the functions on your storyboards
override func viewDidLoad() {
super.viewDidLoad()
txtPassword.enablePasswordToggle()
txtConfirmPassword.enablePasswordToggle()
}

UIbutton inside UIViewControllers inside UIScrollView not getting triggered on touch

I'v added two UIViewControllers inside of a UIScrollView, and now I'm trying to add targets to the button inside of the UIViewControllers, I've checked the internet but none of those solutions worked for me, this is what I'm doing
inside of the main ViewController
scrollView.contentSize = CGSize(width: view.frame.width * 2, height:0)
let postsViewController = PostsViewController()
postsViewController.scrollView = scrollView
postsViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
let chatsViewController = ChatsViewController()
chatsViewController.scrollView = scrollView
chatsViewController.view.frame = CGRect(x: view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(postsViewController.view)
scrollView.addSubview(chatsViewController.view)
postsViewController.didMove(toParent: self)
chatsViewController.didMove(toParent: self)
then inside the postsViewController I'm doing this
let a = UIButton()
a.setImage(currentTheme.chat, for: .normal)
a.imageEdgeInsets = UIEdgeInsets(top: 1.5, left: 0.0, bottom: 1.5, right: 0.0)
view.addSubview(a)
view.addConstraint("V:|-200-[v0(80)]-0-|",a)
view.addConstraint("H:|-200-[v0(80)]-0-|",a)
a.addTarget(self, action: #selector(abc), for: .touchUpInside)
the add "addConstraint" is an extension function just to add constraint to views
the abc function
#objc func abc() {
print("hello")
}
you can see the scrollView setting here
https://ibb.co/ZB1LJsy
Try to initialize your button like this
let a : UIButton = UIButton(type: .custom)
And add this after to the initialize of your UIScrollView
scrollView.delaysContentTouches = false
I tested in an xcode project. I can not execute the function (button) in the other UIViewController.
By cons if you sent the #selector it works.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let scrollView : UIScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width))
scrollView.delaysContentTouches = false
view.addSubview(scrollView)
let TestViewController = TestViewController1()
TestViewController.select = #selector(demande(sender:))
TestViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(TestViewController.view)
print("dd")
}
#objc func demande(sender: UIButton!) {
print("button click", sender.tag)
}
}
class TestViewController1: UIViewController {
var select : Selector!
override func viewDidLoad() {
super.viewDidLoad()
let button : UIButton = UIButton(type: .custom)
button.setImage(#imageLiteral(resourceName: "turn_off_on_power-512"), for: .normal)
button.tag = 2
button.addTarget(self, action: select, for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
view.addSubview(button)
}
}
Hoping that it helps you
okay, I solved my problem, the reason why the button wasn't getting trigger is because I forgot to attach the viewController of the view to the scrollview, so all I had to do is to attach the viewController as well and it worked.
self.addChild(postsViewController)
self.addChild(chatsViewController)
now it is working.

Why is my button not working in Swift Playgrounds?

I am trying to make a playground and I have a button that says "Let's play!" and moves into a new view controller.
I looked at the code from this website and put it into my code:
http://lab.dejaworks.com/ios-swift-3-playground-uibutton-action/
This is all of my code (like, all of it):
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
//Introduction
let view = UIView()
view.backgroundColor = .red
//title
func labelCool() {
let label = UILabel()
label.frame = CGRect(x: 50, y: 300, width: 400, height: 100)
label.text = "Add-Add - A Wonderful Game!"
//label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.numberOfLines = 3
label.font = UIFont(name: "HelveticaNeue-Bold", size: 30)
UILabel.animate (withDuration: 10.0, animations:{
label.textColor = .black
})
UILabel.animate(withDuration: 5.0, animations:{
label.textColor = .blue
})
view.addSubview(label)
}
labelCool()
//subtitle
let subtitle = UILabel()
subtitle.frame = CGRect(x: 50, y: 400, width: 200, height: 50)
subtitle.text = "Curated and Created by Yours Truly, Adit Dayal!"
subtitle.numberOfLines = 4
self.view = view
view.addSubview(subtitle)
}
override func viewDidLoad() {
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
let responder = Responder()
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
view.addSubview(button)
}
}
class gameViewController: UIViewController {
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
For now, I just want the button to display "Yay!" when clicked, but it is doing nothing!
Does anyone know why? (I'm on a bit of a time constraint)
Thank you so much,
Adit Dayal
Your Responder class is inside the viewDidLoad() function put the class outside like so
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
override func viewDidLoad() {
let responder = Responder()
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(responder.action), for: .touchUpInside)
view.addSubview(button)
}
The problem is that you are creating the responder object inside the viewDidLoad, as a local variable; this cause the object to be destroyed when the function ends (but we want that object alive even after). You have to retain that object, so instead of creating a local variable, create an instance variable by simply saving it as a class scope:
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
let responder = Responder() // this is now outside the viewDidLoad, so it's an instance variable
override func viewDidLoad() {
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
view.addSubview(button)
}

Animating UIIMageView Alpha on view did load

I am attempting to animate the background image of my HomeController.
I would like to fade the image in, as such I was hoping to animate it's alpha property.
I have found many answers on here around this, however many seem dated (obj c, iOS 3/4/5 etc) and following the recommended approach, I still cannot get this animation to work.
Can anyone please tell me what is wrong with my code?
I would like to animate backgroundImage. I have placed my animation code at the end of my setupView method.
It does not 'fade in' as I would like, essentially the alpha jumps straight to 0.7
import UIKit
class HomeController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private let backgroundImage: UIImageView = {
let image = UIImageView()
image.image = #imageLiteral(resourceName: "foggy").withRenderingMode(.alwaysOriginal)
image.contentMode = .scaleToFill
image.alpha = 0
image.isOpaque = false
return image
}()
private let currentLocation: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Southampton, UK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 24)
button.contentMode = .center
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(onPressLocation), for: .touchUpInside)
return button
}()
private let currentTemp: UIButton = {
let button = UIButton(type: .system)
button.setTitle("37°C", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 54, weight: .thin)
button.contentMode = .center
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(onPressTemp), for: .touchUpInside)
return button
}()
}
extension HomeController {
#objc fileprivate func onPressLocation() -> Void {
print("Location pressed.")
}
#objc fileprivate func onPressTemp() -> Void {
self.currentTemp.setTitle("98°F", for: .normal)
}
fileprivate func setupView() -> Void {
view.backgroundColor = .white
// MARK: Background Image
view.addSubview(backgroundImage)
backgroundImage.anchor(
top: view.safeAreaLayoutGuide.topAnchor,
left: view.safeAreaLayoutGuide.leftAnchor,
bottom: view.safeAreaLayoutGuide.bottomAnchor,
right: view.safeAreaLayoutGuide.rightAnchor,
paddingTop: 0,
paddingLeft: 0,
paddingBottom: 0,
paddingRight: 0,
width: 0,
height: 0
)
// MARK: Current Location
view.addSubview(currentLocation)
currentLocation.anchor(
top: view.safeAreaLayoutGuide.topAnchor,
left: view.safeAreaLayoutGuide.leftAnchor,
right: view.safeAreaLayoutGuide.rightAnchor,
paddingTop: 50,
paddingLeft: 0,
paddingBottom: 0,
paddingRight: 0,
width: 0,
height: 26,
centerX: view.centerXAnchor
)
// MARK: Temp Button
view.addSubview(currentTemp)
currentTemp.anchor(
top: currentLocation.bottomAnchor,
paddingTop: 10,
paddingLeft: 0,
paddingBottom: 0,
paddingRight: 0,
width: 200,
height: 70,
centerX: view.centerXAnchor
)
// MARK: Animations
UIView.animate(withDuration: 6, delay: 3, options: .curveEaseInOut, animations: {
self.backgroundImage.alpha = 0.7
}, completion: nil)
}
}
IBOutlets does not get visible when viewDidLoad is called so call your setupView method in viewDidAppear which is called after your view appeared on screen.
Hope this helps.
You can put your code in viewDidAppear view life cycle method for any animation work ,because viewDidAppear method notifies the view controller that its view was added to a view hierarchy.Use below code for your solution.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated: animated)
//Call method like this
setupView()
}

Swift Content Tabs?

I am building an app and I am required to have 3 tabs if you click on the first tab, its a tableview, second tab a different tableview, third tab, TextView, here is an image of what I am trying to do:
Every time I google looking for examples of something like this, I get referred to UITabBarController, I don't think a tab bar is what I am looking for.
What you are looking for is called Segmented Control (UISegmentedControl).
You can see it in Action natively e.g. in both the Apple iTunes and Health App.
Instead of performing segues to different views with the UITabBarController, you can, using Interface Builder, connect it to your UIViewController like so
#IBOutlet weak var segmentedControl: UISegmentedControl!
and in
viewDidLoad() {
switch segmentedControl.selectedIndex {
case 0: // Do something on your first picture
someFunction()
case 1: // Do something on your second picture
performSegue(withIdentifier: "your identifier here", sender: nil)
case 2: // Do something on your third picture
image.isHidden = true
button.isEnabled = false
default: break
}
... etc. You could also initiate different View Controllers instead of manipulating just one View, which you can then access via segues.
Please see official Swift doc # https://developer.apple.com/documentation/uikit/uisegmentedcontrol?changes=_3
I would create this layout programmatically. You want a custom look to it so I do not think the standard controls are your best option.
Here is a playground to get you started on one way this could be achieved:
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let view1 = UIView(frame: CGRect(x: 0, y: 100, width: 768, height: 924))
let view2 = UIView(frame: CGRect(x: 0, y: 100, width: 768, height: 924))
let view3 = UIView(frame: CGRect(x: 0, y: 100, width: 768, height: 924))
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x: 0, y: 0, width: 768, height: 1024)
self.view.backgroundColor = .black
// View to have a border around the buttons
let box = UIView(frame: CGRect(x: 40, y: 20, width: 300, height: 50))
box.layer.borderColor = UIColor.white.cgColor
box.layer.borderWidth = 2
self.view.addSubview(box)
// Tab buttons
let button1 = UIButton(frame: CGRect(x: 40, y: 20, width: 100, height: 50))
button1.setTitle("Orange", for: .normal)
button1.tag = 1
button1.addTarget(self, action: #selector(tabTouched(sender:)), for: .touchUpInside)
self.view.addSubview(button1)
let button2 = UIButton(frame: CGRect(x: 140, y: 20, width: 100, height: 50))
button2.setTitle("Blue", for: .normal)
button2.tag = 2
button2.addTarget(self, action: #selector(tabTouched(sender:)), for: .touchUpInside)
self.view.addSubview(button2)
let button3 = UIButton(frame: CGRect(x: 240, y: 20, width: 100, height: 50))
button3.setTitle("Green", for: .normal)
button3.tag = 3
button3.addTarget(self, action: #selector(tabTouched(sender:)), for: .touchUpInside)
self.view.addSubview(button3)
// Tab Views
view1.backgroundColor = .orange
self.view.addSubview(view1)
view2.backgroundColor = .blue
view2.alpha = 0
self.view.addSubview(view2)
view3.backgroundColor = .green
view3.alpha = 0
self.view.addSubview(view3)
}
// When each of the buttons are tapped we will hide or show the correct tab's view
#objc func tabTouched(sender: UIButton) {
if sender.tag == 1 {
UIView.animate(withDuration: 0.3) {
self.view1.alpha = 1
self.view2.alpha = 0
self.view3.alpha = 0
}
} else if sender.tag == 2 {
UIView.animate(withDuration: 0.3) {
self.view1.alpha = 0
self.view2.alpha = 1
self.view3.alpha = 0
}
} else if sender.tag == 3 {
UIView.animate(withDuration: 0.3) {
self.view1.alpha = 0
self.view2.alpha = 0
self.view3.alpha = 1
}
}
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = ViewController()
You can copy and paste this into an empty playground to see it in action. Essentially this is showing or hiding the appropriate views based on which button is selected. You can swap out the views for view controllers if you so desire.

Resources