FBSDKLoginManager withReadPermissions error - ios

I am having an issue with converting the value of a UIcollectionViewCell to the argument type of a UIViewController inside my handleCustomFBButton() function below. I believe it is something wrong with setting my delegates and/or datasource...thanks for any and all help!
import UIKit
import FBSDKLoginKit
import Firebase
class LoginCell: UICollectionViewCell, FBSDKLoginButtonDelegate {
let logoImageView: UIImageView = {
let image = UIImage(named: "me")
let imageView = UIImageView(image: image)
return imageView
}()
let emailTextField: LeftPaddedTextField = {
let textField = LeftPaddedTextField()
textField.placeholder = "Enter Email"
textField.layer.borderColor = UIColor.lightGray.cgColor
textField.layer.borderWidth = 1
textField.keyboardType = .emailAddress
return textField
}()
let passwordTextField: LeftPaddedTextField = {
let textField = LeftPaddedTextField()
textField.placeholder = "Enter Password"
textField.layer.borderColor = UIColor.lightGray.cgColor
textField.layer.borderWidth = 1
textField.isSecureTextEntry = true
return textField
}()
lazy var loginButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .orange
button.setTitle("Log in", for: .normal)
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(handleLogin), for: .touchUpInside)
return button
}()
lazy var FBloginButton: UIButton = {
let customButton = UIButton(type: .system)
customButton.backgroundColor = UIColor.blue
customButton.setTitle("Custom button here", for: .normal)
customButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
customButton.setTitleColor(.white, for: .normal)
return customButton
customButton.addTarget(self, action: #selector(handleCustomFBButton), for: .touchUpInside)
}()
// error happens on the from: self
func handleCustomFBButton() {
FBSDKLoginManager().logIn(withReadPermissions: ["email", "public_profile"], from: self) { (result, err) in
if err != nil {
print("Custom FB Login Button Failed")
return
}
self.showEmailAddress()
}
}

Related

How to pass data forward between View Controllers - Swift

Hi I would like to know why every-time i ask for the emailCatched value in the second VC, I always get nil. Isn't how you pass data forward? Sorry if it's very basic.
I wrote the same code for passing the password from VC2 to VC3 and it works, it's weird. Thanks
First VC :
class EmailAddressViewController: UIViewController {
let emailField: UITextField = {
let emailField = UITextField()
return emailField
}()
private let continueButton: UIButton = {
let button = UIButton()
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
// SubViews
view.backgroundColor = .white
view.frame = view.bounds
view.addSubview(emailField)
view.addSubview(continueButton)
// I omitted the frames
continueButton.addTarget(self,
action: #selector(continueButtonTapped),
for: .touchUpInside)
}
#objc private func continueButtonTapped() {
let email = emailField.text,
let vc = PasswordViewController()
vc.emailCatched = email
self.present(PasswordViewController(), animated: true)
}
}
Second VC (PasswordViewController) :
class PasswordViewController: UIViewController {
private let passwordField: UITextField = {
let passwordField = UITextField()
passwordField.autocapitalizationType = .none
passwordField.autocorrectionType = .no
passwordField.returnKeyType = .next
passwordField.layer.cornerRadius = 10
passwordField.layer.borderWidth = 1
passwordField.layer.borderColor = UIColor.darkGray.cgColor
passwordField.placeholder = "Mot de passe"
passwordField.textAlignment = .center
passwordField.backgroundColor = .white
passwordField.translatesAutoresizingMaskIntoConstraints = false
passwordField.isSecureTextEntry = true
return passwordField
}()
private let continueButton: UIButton = {
let button = UIButton()
button.setTitle("Continuer", for: .normal)
button.backgroundColor = .systemGreen
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 25
button.layer.masksToBounds = true
button.titleLabel?.font = .systemFont(ofSize: 20, weight: .bold)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
var emailCatched: String?
// MARK: viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
print(emailCatched!) // returns error or nil
// SubViews
view.backgroundColor = .white
view.frame = view.bounds
view.addSubview(passwordField)
view.addSubview(continueButton)
continueButton.addTarget(self,
action: #selector(continueButtonTapped),
for: .touchUpInside)
}
// function bouton continuer
#objc private func continueButtonTapped() {
passwordField.resignFirstResponder()
guard let password = passwordField.text,
!password.isEmpty,
password.count >= 8 else {
alertInvalidPassword()
return
}
let vc = NamesViewController()
// vc.emailCatched = emailCatched!
vc.passwordCatched = password
HapticsManager.shared.vibrate(for: .success)
self.presenter(vc, animated: false, pushing: true, completion: nil)
}
}

How to create a custom iOS keyboard

First off, I will have you know that I have looked at many many different posts on StackOverflow and on Google & Github. I have scoured everywhere for ANYTHING that will help me. But, nothing seems to work. Either it's out of date (by 10+ years) or written entirely in Objective-C. I know that there are other posts out there about this topic, but I need a solution in Swift 5.0 +, not an ancient one from 11 years ago in Obj-C that is totally deprecated in everything today.
Now, my question. I need to develop a keyboard for my iOS app. It needs to be a number/operator type keyboard, effectively a basic maths keyboard.
I have no idea how or where to start, I am relatively new to Swift/iOS development in general.
I have tried to use KeyboardKit, (See Github Page) but have had no luck; the documentation is extremely minimal, not anywhere close to enough for a beginner to take and use effectively.
I have tried many different GitHub repos, but none have fit what I need.
In a summary, my keyboard needs to be set as the default keyboard for the app, (but I'll ask this a a different question later); so it needs that option. It needs to be fully (easily =) customizable. And it needs to act just like the default Apple keyboard.
I am looking for something relevant, and extensible
Cheers!
I have solved my problem. I discovered this site
on StackOverflow that completely solved my issues. I was able to add customized keys to the keyboard, which was my primary issue.
I created a class called DigitButton.swift.
import UIKit
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: (UIKeyInput & UITextInput)?
var useDecimalSeparator: Bool
lazy var parenthesis1: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "("
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapParenthesis1(_:)), for: .touchUpInside)
return button
}()
lazy var squareroot: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "√"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapSquareRoot(_:)), for: .touchUpInside)
return button
}()
lazy var parenthesis2: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = ")"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapParenthesis2), for: .touchUpInside)
return button
}()
lazy var exponentButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^0"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton2: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^2"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton2(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton3: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^3"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton3(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton4: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton4(_:)), for: .touchUpInside)
return button
}()
lazy var addButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "+"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapAddButton(_:)), for: .touchUpInside)
return button
}()
lazy var subtractButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "-"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapSubtractButton(_:)), for: .touchUpInside)
return button
}()
lazy var divideButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "/"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapDivideButton(_:)), for: .touchUpInside)
return button
}()
lazy var multiplyButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "*"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapMultiplyButton(_:)), for: .touchUpInside)
return button
}()
lazy var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = $0
button.setTitle("\($0)", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .title1)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.sizeToFit()
button.titleLabel?.numberOfLines = 1
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.lineBreakMode = .byTruncatingTail
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.addTarget(self, action: #selector(didTapDigitButton(_:)), for: .touchUpInside)
button.inputView.self?.sizeToFit()
return button
}
var deleteButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("⌫", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = "Delete"
button.addTarget(self, action: #selector(didTapDeleteButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput & UITextInput, useDecimalSeparator: Bool = false) {
self.target = target
self.useDecimalSeparator = useDecimalSeparator
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension NumericKeyboard {
#objc func didTapSquareRoot(_ sender: DigitButton) {
insertText("√")
}
#objc func didTapParenthesis1(_ sender: DigitButton) {
insertText("(")
}
#objc func didTapParenthesis2(_ sender: DigitButton) {
insertText(")")
}
#objc func didTapDigitButton(_ sender: DigitButton) {
insertText("\(sender.digit)")
}
#objc func didTapDecimalButton(_ sender: DigitButton) {
insertText(Locale.current.decimalSeparator ?? ".")
}
#objc func didTapExponentButton(_ sender: DigitButton){
insertText("^0")
}
#objc func didTapExponentButton2(_ sender: DigitButton){
insertText("^2")
}
#objc func didTapExponentButton3(_ sender: DigitButton){
insertText("^3")
}
#objc func didTapExponentButton4(_ sender: DigitButton){
insertText("^")
}
#objc func didTapAddButton(_ sender: DigitButton){
insertText("+")
}
#objc func didTapSubtractButton(_ sender: DigitButton){
insertText("-")
}
#objc func didTapDivideButton(_ sender: DigitButton){
insertText("/")
}
#objc func didTapMultiplyButton(_ sender: DigitButton){
insertText("*")
}
#objc func didTapDeleteButton(_ sender: DigitButton) {
target?.deleteBackward()
}
}
// MARK: - Private initial configuration methods
private extension NumericKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
addButtons()
}
func addButtons() {
let stackView = createStackView(axis: .vertical)
stackView.frame = bounds
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(stackView)
for row in 0 ..< 3 {
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
for column in 0 ..< 3 {
subStackView.addArrangedSubview(numericButtons[row * 3 + column + 1])
}
}
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
subStackView.addArrangedSubview(numericButtons[0])
subStackView.addArrangedSubview(parenthesis1)
subStackView.addArrangedSubview(parenthesis2)
subStackView.addArrangedSubview(squareroot)
subStackView.addArrangedSubview(addButton)
subStackView.addArrangedSubview(subtractButton)
subStackView.addArrangedSubview(multiplyButton)
subStackView.addArrangedSubview(divideButton)
subStackView.addArrangedSubview(exponentButton)
subStackView.addArrangedSubview(exponentButton2)
subStackView.addArrangedSubview(exponentButton4)
subStackView.addArrangedSubview(deleteButton)
}
func createStackView(axis: NSLayoutConstraint.Axis) -> UIStackView {
let stackView = UIStackView()
stackView.axis = axis
stackView.alignment = .fill
stackView.distribution = .fillProportionally
return stackView
}
func insertText(_ string: String) {
guard let range = target?.selectedRange else { return }
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string)
}
}
// MARK: - UITextInput extension
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
Then I set my InputView input method with textField.inputView = NumericKeyboard(target: textField)
This has worked perfectly.

Question about disabling one button when one button is active in Swift

The picture above has two buttons.
The background is filled in red with the left button pressed.
If I press right button here
I want the background of the right button to be filled with red, the left button to be white as the right button, and the button to be deactivated.
override func viewDidLoad() {
super.viewDidLoad()
bookTitleFilterBtn.addTarget(self, action: #selector(bookTitleFilterBtnClicked(_:)), for: .touchUpInside)
authorNameFilterBtn.addTarget(self, action: #selector(authorNameFilterBtnClicked(_:)), for: .touchUpInside)
}
//left button
#objc func bookTitleFilterBtnClicked(_ sender: UIButton) {
DispatchQueue.main.async {
if self.isHighlighted == false {
sender.backgroundColor = .red
let title = NSAttributedString(string: "제목", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])
sender.setAttributedTitle(title, for: .normal)
sender.isHighlighted = true
self.isHighlighted = true
} else {
sender.backgroundColor = .white
let title = NSAttributedString(string: "제목", attributes: [NSAttributedString.Key.foregroundColor: UIColor.black])
sender.setAttributedTitle(title, for: .normal)
sender.isHighlighted = false
self.isHighlighted = false
}
}
}
//right button
#objc func authorNameFilterBtnClicked(_ sender: UIButton) {
DispatchQueue.main.async {
if self.isHighlighted == false {
sender.isHighlighted = true
let title = NSAttributedString(string: "작가", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])
sender.setAttributedTitle(title, for: .normal)
sender.backgroundColor = .red
self.isHighlighted = true
} else {
sender.isHighlighted = false
self.isHighlighted = false
let title = NSAttributedString(string: "작가", attributes: [NSAttributedString.Key.foregroundColor: UIColor.black])
sender.setAttributedTitle(title, for: .normal)
sender.backgroundColor = .white
}
}
}
You forgot to change the backgroundColor in the first condition of the first method. To prevent more of these kind of issues, try to define the logic in a function and call it anywhere you need instead of rewriting it over and over:
override func viewDidLoad() {
super.viewDidLoad()
bookTitleFilterBtn.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside)
authorNameFilterBtn.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside)
}
var buttons: [UIButton] { return [bookTitleFilterBtn, authorNameFilterBtn] }
func updateButtonsAppearance(allButtons: [UIButton], selectedButton: UIButton) {
for button in allButtons {
let isSelected = button == selectedButton
let currentTitle = button.currentTitle ?? "-"
let title = NSAttributedString(string: currentTitle, attributes: [.foregroundColor: isSelected ? UIColor.white : UIColor.black])
button.setAttributedTitle(title, for: .normal)
button.backgroundColor = isSelected ? .red : .white
button.isHighlighted = isSelected
}
}
#objc func buttonClicked(_ sender: UIButton) {
DispatchQueue.main.async {
self.updateButtonsAppearance(allButtons: buttons, selectedButton: sender)
}
}
Note that both buttons are now calling same function. So there is only one source of truth now. If it works somewhere, it works everywhere.
You are missing following line of code to change backgroundColor of another button. Add following line of code.
//left button
#objc func bookTitleFilterBtnClicked(_ sender: UIButton) {
DispatchQueue.main.async {
if self.isHighlighted == false {
....
....
authorNameFilterBtn.backgroundColor = .white
} else {
....
}
}
}
//right button
#objc func authorNameFilterBtnClicked(_ sender: UIButton) {
DispatchQueue.main.async {
if self.isHighlighted == false {
....
bookTitleFilterBtn.backgroundColor = .white
} else {
....
}
}
}
This code will change color of buttons vice-versa
set left button default selected from StoryBoard
var selectedButton:String = "" // gloable variable
//left button
#objc func bookTitleFilterBtnClicked(_ sender: UIButton) {
if selectedButton != "제목"
{
selectedButton = "제목"
sender.backgroundColor = .red
let title = NSAttributedString(string: "제목", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])//title
sender.setAttributedTitle(title, for: .normal)
sender.isHighlighted = true
self.isHighlighted = true
authorNameFilterBtn.backgroundColor = .white
let title1 = NSAttributedString(string: "작가", attributes: [NSAttributedString.Key.foregroundColor: UIColor.black])//title
authorNameFilterBtn.setAttributedTitle(title1, for: .normal)
authorNameFilterBtn.isHighlighted = false
self.isHighlighted = false
}
}
//right button
#objc func authorNameFilterBtnClicked(_ sender: UIButton) {
if selectedButton != "작가"
{
selectedButton = "작가"
sender.isHighlighted = true
let title = NSAttributedString(string: "작가", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])//Author
sender.setAttributedTitle(title, for: .normal)
sender.backgroundColor = .red
self.isHighlighted = true
bookTitleFilterBtn.isHighlighted = false
self.isHighlighted = false
let title1 = NSAttributedString(string: "제목", attributes: [NSAttributedString.Key.foregroundColor: UIColor.black])
bookTitleFilterBtn.setAttributedTitle(title1, for: .normal)
bookTitleFilterBtn.backgroundColor = .white
}

addTarget is not working for button in UICollectionViewCell

When I m clicking on button , selector is not getting called.
There are only two component is the cell , 1 is image and another is UIButton.
Below is the code for collection cell. is there any other way to add method.
class AttachmentCell: UICollectionViewCell {
weak var delegate: AttachmentCellDelegate?
let removeButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "close_icon"), for: .normal)
button.addTarget(self, action: #selector(removeButtonTapped), for: .touchUpInside)
button.isUserInteractionEnabled = true
return button
}()
let imageView: UIImageView = {
let imgView = UIImageView()
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
return imgView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.red
self.isUserInteractionEnabled = true
self.contentView.addSubview(imageView)
imageView.isUserInteractionEnabled = true
self.contentView.addSubview(removeButton)
//self.addSubview(removeButton)
imageView.snp.makeConstraints { (make) in
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
removeButton.snp.makeConstraints { (make) in
make.width.equalTo(40)
make.height.equalTo(40)
make.top.equalTo(imageView.snp.top).offset(-5)
make.trailing.equalTo(self.imageView.snp.trailing).offset(5)
}
self.backgroundColor = UIColor.gray
}
#objc func removeButtonTapped() {
delegate?.didRemoveButtonTapped()
}
}
Change let removeButton to lazy var removeButton.
self doesn't exist until init has been called. When you add a target to self in a let constant, you are defining it before init has been called.
Alternatively, just call addTarget in the init block.

Action not being called when button is tapped in a stack view

I have a custom view that includes a stack view. Inside the stack view I have a label and a button.
I created my stack view, label and button in the following way and added them to the parent view.
class HomeView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = false
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
...
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(HomeController.loginClicked(_:)), for: .touchUpInside)
return button
}()
}
In my view controller I add the view to the controller's base view and set the constraints. I also create the method that should be called when the signin button is tapped.
override func viewDidLoad() {
super.viewDidLoad()
homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(homeView)
homeView.fullscreenView(parentView: view)
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
When I press the button the loginClicked method is not called. Now I did tried moving the loginClicked method to the custom view and changing the addTarget accordingly and loginClicked method is called. This being said I know the button is clickable but I don't think the target for the button action is correct and that is why the loginClicked method in the view controller is not being called.
You can use Protocol/Delegation
//1. Create a protocol
protocol HomeViewDelegate{
func loginButtonClicked(sender: UIButton)
}
class HomeView: UIView {
//2. Create a delegate
var delegate: HomeViewDelegate?
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = false
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(loginClicked(sender:)), for: .touchUpInside)
button.backgroundColor = .red
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//3. Call your protocol method via delegate
#objc func loginClicked(sender: UIButton) {
if let delegate = delegate{
delegate.loginButtonClicked(sender: sender)
}
}
}
In You Caller ViewController create an extension
extension ViewController: HomeViewDelegate{
func loginButtonClicked(sender: UIButton) {
print("login Button Clicked")
}
}
First of all you set userInteractionEnabled property of your stackView to false, set it to true. Then if it does not work consider the following approach:
There are two possible ways to fix this, first is adding the target from ViewController, and the other one is using delegation.
I think the first way would be easier to implement for you.
You need to add your target from your ViewController class.
First update your view class and get rid of adding a target:
class HomeView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = true
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
...
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
return button
}()
}
Now in your ViewController:
override func viewDidLoad() {
super.viewDidLoad()
homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
homeView.signIn.addTarget(self, action: #selector(loginClicked), for: .touchUpInside)
view.addSubview(homeView)
homeView.fullscreenView(parentView: view)
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
You need to add the right constraints, I had this problem, I had this problem and the solution was this.
Solution:
import Foundation
import UIKit
protocol HomeViewDelegate:class{
func loginButtonClicked(sender: UIButton)
}
class HomeView: UIView {
//2. Create a delegate
weak var delegate: HomeViewDelegate?
var stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.axis = .vertical
stack.isUserInteractionEnabled = true
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
label.backgroundColor = .gray
label.text = "Label"
label.textAlignment = .center
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(loginClicked(sender:)), for: .touchUpInside)
button.isUserInteractionEnabled = true
button.backgroundColor = .red
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
addSubview(stackView)
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: self.topAnchor),
self.stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
self.stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//3. Call your protocol method via delegate
#objc func loginClicked(sender: UIButton) {
if let delegate = delegate{
delegate.loginButtonClicked(sender: sender)
}
}
}
ViewController
override func viewDidLoad() {
super.viewDidLoad()
let homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
homeView.delegate = self
self.view.addSubview(homeView)
NSLayoutConstraint.activate([
homeView.centerXAnchor.constraint(equalTo: centerXAnchor),
homeView.centerYAnchor.constraint(equalTo: centerYAnchor),
homeView.heightAnchor.constraint(equalToConstant: 300),
homeView.widthAnchor.constraint(equalToConstant: 300)
])
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
add this line to your button code
button.isUserInteractionEnabled = true
re-active isUserInteractionEnabled again
let signin: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(ViewController.loginClicked(_:)), for: .touchUpInside)
button.isUserInteractionEnabled = true
return button
}()

Resources