I have the following notification setup in my viewDidLoad() of my custom keyboard extension:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
and this is the function I call outside of my viewDidLoad():
#objc func keyboardWillShow(_ notification: NSNotification) {
if let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
print(keyboardRect.height)
ScreenHeight = Int(keyboardRect.height)
}
}
However, I set a breakpoint inside of my keyboardWillShow() function and it is never triggered. Is there something else I have to do to get the size of the keyboard before it shows?
I am trying to dynamically adjust the size of elements programmatically inserted into my keyboard.
Let me know if more information is necessary or if this is an obvious fix.
Full Code:
class KeyboardViewController: UIInputViewController {
#IBOutlet var nextKeyboardButton: UIButton! = UIButton(type: .system)
var centralManager: CBCentralManager!
var NUB5: CBPeripheral!
var characteristics = [CBCharacteristic]()
var connection: UILabel = UILabel()
var serialNum: UILabel = UILabel()
var serial: String = ""
var ScreenHeight = 0
let cornerRadii = 4
//setup Number buttons
var num1: UIButton = UIButton()
var num2: UIButton = UIButton()
var num3: UIButton = UIButton()
var num4: UIButton = UIButton()
var num5: UIButton = UIButton()
var num6: UIButton = UIButton()
var num7: UIButton = UIButton()
var num8: UIButton = UIButton()
var num9: UIButton = UIButton()
var num0: UIButton = UIButton()
var spaceBar: UIButton = UIButton()
var plus: UIButton = UIButton()
var minus: UIButton = UIButton()
var back: UIButton = UIButton()
var left: UIButton = UIButton()
var right: UIButton = UIButton()
var period: UIButton = UIButton()
var comma: UIButton = UIButton()
var returnKey: UIButton = UIButton()
let screensize: CGRect = UIScreen.main.bounds
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
var keyboard: UIView!
func loadInterface() {
// load the nib file
var calculatorNib = UINib(nibName: "keyboard", bundle: nil)
// instantiate the view
keyboard = (calculatorNib.instantiate(withOwner: self, options: nil)[0] as! UIView)
// add the interface to the main view
view.addSubview(keyboard)
// copy the background color
view.backgroundColor = keyboard.backgroundColor
ScreenHeight = Int(self.accessibilityFrame.height)
print(ScreenHeight)
}
super.viewDidLoad()
loadInterface()
self.centralManager = CBCentralManager(delegate: self, queue: nil)
//Setup Labels
let width = screensize.width
let height = CGFloat(200) //subtract 10 from total screensize to give space for connection indicator
let screensize = self.view.bounds.size
//ScreenHeight = Int(height)
connection.frame = CGRect(x: 0,y: 0,width: width,height: 10)
//TODO
serialNum.frame = CGRect(x: width/2+5, y:10,width: width/2 - 10,height:15)
spaceBar.frame = CGRect(x: width*4/5+4, y: height/4+10, width: width/5, height: height/4)
num1.frame = CGRect(x:width/5,y: 10, width: width/5,height: height/4)
num2.frame = CGRect(x:width*2/5,y: 10, width: width/5,height: height/4)
num3.frame = CGRect(x:width*3/5,y: 10, width: width/5,height: height/4)
num4.frame = CGRect(x:width/5+1,y: height/4+10, width: width/5,height: height/4)
num5.frame = CGRect(x:width*2/5+2,y: height/4+10, width: width/5,height: height/4)
num6.frame = CGRect(x:width*3/5+3,y: height/4+10, width: width/5,height: height/4)
num7.frame = CGRect(x:width/5+1,y: height/2+10, width: width/5,height: height/4)
num8.frame = CGRect(x:width*2/5+2,y: height/2+10, width: width/5,height: height/4)
num9.frame = CGRect(x:width*3/5+3,y: height/2+10, width: width/5,height: height/4)
num0.frame = CGRect(x:width*2/5+3,y: 3*height/4+10, width: width/5,height: height/4)
plus.frame = CGRect(x:0,y: 10, width: width/5,height: height/4)
minus.frame = CGRect(x:0,y: height/4+10, width: width/5,height: height/4)
back.frame = CGRect(x:width*4/5,y:10,width:width/5,height:height/4)
right.frame = CGRect(x: width*4/5+4, y: height/2+10, width: width/5, height: height/4)
left.frame = CGRect(x:0,y: height/2+10, width: width/5,height: height/4)
nextKeyboardButton.frame = CGRect(x:0,y: 3*height/4+10, width: width/5,height: height/4)
period.frame = CGRect(x:width/5,y: 3*height/4+10, width: width/5,height: height/4)
comma.frame = CGRect(x:width*3/5+2,y: 3*height/4+10, width: width/5,height: height/4)
returnKey.frame = CGRect(x:width*4/5+3,y: height*3/4+10, width: width/5,height: height/4)
num1.addTarget(self,action: #selector(addOne), for: .touchUpInside)
num2.addTarget(self,action: #selector(addTwo), for: .touchUpInside)
num3.addTarget(self,action: #selector(addThree), for: .touchUpInside)
num4.addTarget(self,action: #selector(addFour), for: .touchUpInside)
num5.addTarget(self,action: #selector(addFive), for: .touchUpInside)
num6.addTarget(self,action: #selector(addSix), for: .touchUpInside)
num7.addTarget(self,action: #selector(addSeven), for: .touchUpInside)
num8.addTarget(self,action: #selector(addEight), for: .touchUpInside)
num9.addTarget(self,action: #selector(addNine), for: .touchUpInside)
num0.addTarget(self,action: #selector(addZero), for: .touchUpInside)
spaceBar.addTarget(self,action: #selector(space), for: .touchUpInside)
plus.addTarget(self, action: #selector(plusSign), for: .touchUpInside)
minus.addTarget(self, action: #selector(minusSign), for: .touchUpInside)
back.addTarget(self, action: #selector(deleteOne), for: .touchUpInside)
left.addTarget(self, action: #selector(leftOne), for: .touchUpInside)
right.addTarget(self, action: #selector(rightOne), for: .touchUpInside)
nextKeyboardButton.addTarget(self, action: #selector(handleInputModeList(from:with:)), for: .allTouchEvents)
period.addTarget(self, action: #selector(addPeriod), for: .touchUpInside)
comma.addTarget(self, action: #selector(addComma), for: .touchUpInside)
returnKey.addTarget(self, action: #selector(returnButton), for: .touchUpInside)
num1.setTitle("1", for: .normal)
num2.setTitle("2", for: .normal)
num3.setTitle("3", for: .normal)
num4.setTitle("4", for: .normal)
num5.setTitle("5", for: .normal)
num6.setTitle("6", for: .normal)
num7.setTitle("7", for: .normal)
num8.setTitle("8", for: .normal)
num9.setTitle("9", for: .normal)
num0.setTitle("0", for: .normal)
spaceBar.setTitle("SPACE", for: .normal)
back.setTitle("<-", for: .normal)
plus.setTitle("+", for: .normal)
minus.setTitle("-", for: .normal)
left.setTitle("<", for: .normal)
right.setTitle(">", for: .normal)
nextKeyboardButton.setTitle("NEXT", for: .normal)
period.setTitle(".", for: .normal)
comma.setTitle(",", for: .normal)
returnKey.setTitle("Return", for: .normal)
num1.layer.cornerRadius = CGFloat(cornerRadii)
num2.layer.cornerRadius = CGFloat(cornerRadii)
num3.layer.cornerRadius = CGFloat(cornerRadii)
num4.layer.cornerRadius = CGFloat(cornerRadii)
num5.layer.cornerRadius = CGFloat(cornerRadii)
num6.layer.cornerRadius = CGFloat(cornerRadii)
num7.layer.cornerRadius = CGFloat(cornerRadii)
num8.layer.cornerRadius = CGFloat(cornerRadii)
num9.layer.cornerRadius = CGFloat(cornerRadii)
num0.layer.cornerRadius = CGFloat(cornerRadii)
spaceBar.layer.cornerRadius = CGFloat(cornerRadii)
plus.layer.cornerRadius = CGFloat(cornerRadii)
minus.layer.cornerRadius = CGFloat(cornerRadii)
back.layer.cornerRadius = CGFloat(cornerRadii)
left.layer.cornerRadius = CGFloat(cornerRadii)
right.layer.cornerRadius = CGFloat(cornerRadii)
nextKeyboardButton.layer.cornerRadius = CGFloat(cornerRadii)
period.layer.cornerRadius = CGFloat(cornerRadii)
comma.layer.cornerRadius = CGFloat(cornerRadii)
returnKey.layer.cornerRadius = CGFloat(cornerRadii)
num5.layer.borderWidth = CGFloat(1)
num1.setTitleColor(UIColor.black, for: .normal)
num2.setTitleColor(UIColor.black, for: .normal)
num3.setTitleColor(UIColor.black, for: .normal)
num4.setTitleColor(UIColor.black, for: .normal)
num5.setTitleColor(UIColor.black, for: .normal)
num6.setTitleColor(UIColor.black, for: .normal)
num7.setTitleColor(UIColor.black, for: .normal)
num8.setTitleColor(UIColor.black, for: .normal)
num9.setTitleColor(UIColor.black, for: .normal)
num0.setTitleColor(UIColor.black, for: .normal)
spaceBar.setTitleColor(UIColor.black, for: .normal)
plus.setTitleColor(UIColor.black, for: .normal)
minus.setTitleColor(UIColor.black, for: .normal)
back.setTitleColor(UIColor.black, for: .normal)
left.setTitleColor(UIColor.black, for: .normal)
right.setTitleColor(UIColor.black, for: .normal)
period.setTitleColor(UIColor.black, for: .normal)
comma.setTitleColor(UIColor.black, for: .normal)
nextKeyboardButton.setTitleColor(UIColor.black, for: .normal)
returnKey.setTitleColor(UIColor.black, for: .normal)
num1.backgroundColor = UIColor.white
num2.backgroundColor = UIColor.white
num3.backgroundColor = UIColor.white
num4.backgroundColor = UIColor.white
num5.backgroundColor = UIColor.white
num6.backgroundColor = UIColor.white
num7.backgroundColor = UIColor.white
num8.backgroundColor = UIColor.white
num9.backgroundColor = UIColor.white
num0.backgroundColor = UIColor.white
spaceBar.backgroundColor = UIColor.white
plus.backgroundColor = UIColor.white
minus.backgroundColor = UIColor.white
back.backgroundColor = UIColor.white
left.backgroundColor = UIColor.white
right.backgroundColor = UIColor.white
nextKeyboardButton.backgroundColor = UIColor.white
period.backgroundColor = UIColor.white
comma.backgroundColor = UIColor.white
returnKey.backgroundColor = UIColor.white
connection.text = "No Connection"
serialNum.text = "-"
connection.backgroundColor = UIColor.white
serialNum.backgroundColor = UIColor.white
connection.layer.cornerRadius = 4
serialNum.layer.cornerRadius = 4
self.view.addSubview(connection)
self.view.addSubview(serialNum)
self.view.addSubview(spaceBar)
self.view.addSubview(num1)
self.view.addSubview(num2)
self.view.addSubview(num3)
self.view.addSubview(num4)
self.view.addSubview(num5)
self.view.addSubview(num6)
self.view.addSubview(num7)
self.view.addSubview(num8)
self.view.addSubview(num9)
self.view.addSubview(num0)
self.view.addSubview(plus)
self.view.addSubview(minus)
self.view.addSubview(back)
self.view.addSubview(left)
self.view.addSubview(right)
self.view.addSubview(period)
self.view.addSubview(comma)
self.view.addSubview(nextKeyboardButton)
self.view.addSubview(returnKey)
}
#objc func returnButton(sender: UIButton){
textDocumentProxy.insertText("\n")
}
#objc func addPeriod(sender: UIButton){
textDocumentProxy.insertText(".")
}
#objc func addComma(sender: UIButton){
textDocumentProxy.insertText(",")
}
#objc func leftOne(sender: UIButton){
textDocumentProxy.adjustTextPosition(byCharacterOffset: -1)
}
#objc func rightOne(sender: UIButton){
textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)
}
#objc func deleteOne(sender: UIButton){
textDocumentProxy.deleteBackward()
}
#objc func plusSign(sender: UIButton){
textDocumentProxy.insertText("+")
}
#objc func minusSign(sender: UIButton){
textDocumentProxy.insertText("-")
}
#objc func space(sender: UIButton){
textDocumentProxy.insertText(" ")
}
#objc func addOne(sender: UIButton){
textDocumentProxy.insertText("1")
textDocumentProxy.insertText(String(ScreenHeight))
}
#objc func addTwo(sender: UIButton){
textDocumentProxy.insertText("2")
}
#objc func addThree(sender: UIButton){
textDocumentProxy.insertText("3")
}
#objc func addFour(sender: UIButton){
textDocumentProxy.insertText("4")
}
#objc func addFive(sender: UIButton){
textDocumentProxy.insertText("5")
}
#objc func addSix(sender: UIButton){
textDocumentProxy.insertText("6")
}
#objc func addSeven(sender: UIButton){
textDocumentProxy.insertText("7")
}
#objc func addEight(sender: UIButton){
textDocumentProxy.insertText("8")
}
#objc func addNine(sender: UIButton){
textDocumentProxy.insertText("9")
}
#objc func addZero(sender: UIButton){
textDocumentProxy.insertText("0")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.centralManager.state == CBManagerState.poweredOn {
self.centralManager.scanForPeripherals(withServices: [lairdUUID], options: nil)
}
}
#objc func keyboardWillShow(_ notification: NSNotification) {
if let userInfo = notification.userInfo as? Dictionary<String, AnyObject>{
let frame = userInfo[UIResponder.keyboardFrameEndUserInfoKey]
let keyBoardRect = frame?.cgRectValue
ScreenHeight = Int(keyBoardRect!.height)
}
}
override func viewWillDisappear(_ animated: Bool) {
// super.viewWillDisappear(animated)
// if let centralManager = self.centralManager {
// if centralManager.state == CBManagerState.poweredOn {
// if let NUB5 = self.NUB5 {
// if peripheral.state == CBPeripheralState.connected {
// centralManager.cancelPeripheralConnection(NUB5)
// }
// }
// }
// }
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(_ textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(_ textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
}
For prevent crashes and for the best practice use
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
on viewWillAppear.
Since your method keyboardWillShow has a parameter you should change the syntax accordingly to inform the compiler exactly the method that it has to look for.
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
What was happening is that the compiler will search for the method keyboardWillShow without any parameters which is not there, Hence doesn't execute anything.
Related
The code below shows a potential login screen and everything works except for the buttons. I am trying to figure out why that is......
I thought maybe it was the version of Xcode i was using(Xcode 13), but the still doesn't seem like the reason or i would have received an error message.
import UIKit
class LoginViewController: UIViewController, UITextFieldDelegate{
struct Constants {
static let cornerRadius: CGFloat = 8.0
}
//Declaration of login fields
override func viewDidLoad() {
super.viewDidLoad()
loginButton.addTarget(self, action: #selector(didTaploginButton), for: .touchUpInside)
createAccountButton.addTarget(self, action: #selector(didTapcreateAccountButton), for: .touchUpInside)
termsButton.addTarget(self, action: #selector(didTaptermsButton), for: .touchUpInside)
privacyButton.addTarget(self, action: #selector(didTapprivacyButton), for: .touchUpInside)
usernameEmailField.delegate = self
passwordField.delegate = self
addSubviews()
view.backgroundColor = .systemBackground
// Do any additional setup after loading the view.
}
private let usernameEmailField: UITextField = {
let field = UITextField()
field.placeholder = "Username or Email"
field.returnKeyType = .next
field.leftViewMode = .always
field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 0))
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.layer.masksToBounds = true
field.layer.cornerRadius = Constants.cornerRadius
field.backgroundColor = .secondarySystemBackground
return field
}()
private let passwordField: UITextField = {
let field = UITextField()
field.placeholder = "Password"
field.returnKeyType = .next
field.leftViewMode = .always
field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 0))
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.layer.masksToBounds = true
field.layer.cornerRadius = Constants.cornerRadius
field.backgroundColor = .secondarySystemBackground
return field
}()
private let loginButton: UIButton = {
let loginbutton = UIButton()
loginbutton.setTitle("Log In", for: .normal)
loginbutton.layer.masksToBounds = true
loginbutton.layer.cornerRadius = Constants.cornerRadius
loginbutton.backgroundColor = .link
loginbutton.setTitleColor(.white, for: .normal)
return UIButton()
}()
private let termsButton: UIButton = {
let button = UIButton()
button.setTitle("Terms of Service", for: .normal)
button.setTitleColor(.secondaryLabel, for: .normal)
return UIButton()
}()
private let privacyButton: UIButton = {
return UIButton()
}()
private let createAccountButton: UIButton = {
let button = UIButton()
button.setTitleColor(.lightGray, for: .normal)
button.setTitle("New User? Create an Account", for: .normal)
button.backgroundColor = .systemBlue
button.layer.cornerRadius = Constants.cornerRadius
button.setTitleColor(.label, for: .normal)
return UIButton()
}()
//Picture behind the Logo
private let headerView: UIView = {
let header = UIView()
header.clipsToBounds = true
let backgroundImageView = UIImageView(image: UIImage(named: "Bluewater"))
header.addSubview(backgroundImageView)
return header
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//assigning frames
headerView.frame = CGRect(x: 0,
y: 0.0,
width: view.width,
height: view.height/3.0
)
usernameEmailField.frame = CGRect(x: 25,
y: headerView.bottom + 10,
width: view.width-50,
height: 52.0
)
passwordField.frame = CGRect(x: 25,
y: usernameEmailField.bottom + 10,
width: view.width-50,
height: 52.0
)
loginButton.frame = CGRect(x: 25,
y: passwordField.bottom + 10,
width: view.width-50,
height: 52.0
)
createAccountButton.frame = CGRect(x: 25,
y: loginButton.bottom + 10,
width: view.width-50,
height: 52.0
)
termsButton.frame = CGRect(x: 10, y: view.height-view.safeAreaInsets.bottom-100, width: view.width-20, height: 50)
configurationView()
}
private func configurationView() {
guard headerView.subviews.count == 1 else {
return
}
guard let backgroundView = headerView.subviews.first else {
return
}
backgroundView.frame = headerView.bounds
//Pic Pool Logo
let imageView = UIImageView(image: UIImage(named: "ClearLogo"))
headerView.addSubview(imageView)
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: headerView.width/4.0,
y: view.safeAreaInsets.top,
width: headerView.width/2.0,
height: headerView.height - view.safeAreaInsets.top)
}
private func addSubviews(){
view.addSubview(usernameEmailField)
view.addSubview(passwordField)
view.addSubview(loginButton)
view.addSubview(termsButton)
view.addSubview(privacyButton)
view.addSubview(createAccountButton)
view.addSubview(headerView)
}
#objc private func didTaploginButton() {}
#objc private func didTaptermsButton() {}
#objc private func didTapprivacyButton() {}
#objc private func didTapcreateAccountButton() {}
}
Take a real hard look at...
private let loginButton: UIButton = {
let loginbutton = UIButton()
loginbutton.setTitle("Log In", for: .normal)
loginbutton.layer.masksToBounds = true
loginbutton.layer.cornerRadius = Constants.cornerRadius
loginbutton.backgroundColor = .link
loginbutton.setTitleColor(.white, for: .normal)
return UIButton()
}()
see anything wrong? Maybe focus in on return UIButton()
So, changing it to...
private let loginButton: UIButton = {
let loginbutton = UIButton()
loginbutton.setTitle("Log In", for: [])
loginbutton.layer.masksToBounds = true
loginbutton.layer.cornerRadius = Constants.cornerRadius
loginbutton.backgroundColor = .link
loginbutton.setTitleColor(.white, for: [])
return loginbutton
}()
will produce...
Runnable example
//
// ViewController.swift
// StackOverflow
//
// Created by Shane Whitehead on 4/5/2022.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate{
struct Constants {
static let cornerRadius: CGFloat = 8.0
}
//Declaration of login fields
override func viewDidLoad() {
super.viewDidLoad()
loginButton.addTarget(self, action: #selector(didTaploginButton), for: .touchUpInside)
createAccountButton.addTarget(self, action: #selector(didTapcreateAccountButton), for: .touchUpInside)
termsButton.addTarget(self, action: #selector(didTaptermsButton), for: .touchUpInside)
privacyButton.addTarget(self, action: #selector(didTapprivacyButton), for: .touchUpInside)
usernameEmailField.delegate = self
passwordField.delegate = self
addSubviews()
view.backgroundColor = .systemBackground
// Do any additional setup after loading the view.
}
private let usernameEmailField: UITextField = {
let field = UITextField()
field.placeholder = "Username or Email"
field.returnKeyType = .next
field.leftViewMode = .always
field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 0))
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.layer.masksToBounds = true
field.layer.cornerRadius = Constants.cornerRadius
field.backgroundColor = .secondarySystemBackground
return field
}()
private let passwordField: UITextField = {
let field = UITextField()
field.placeholder = "Password"
field.returnKeyType = .next
field.leftViewMode = .always
field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 0))
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.layer.masksToBounds = true
field.layer.cornerRadius = Constants.cornerRadius
field.backgroundColor = .secondarySystemBackground
return field
}()
private let loginButton: UIButton = {
let loginbutton = UIButton()
loginbutton.setTitle("Log In", for: [])
loginbutton.layer.masksToBounds = true
loginbutton.layer.cornerRadius = Constants.cornerRadius
loginbutton.backgroundColor = .link
loginbutton.setTitleColor(.white, for: [])
return loginbutton
}()
private let termsButton: UIButton = {
let button = UIButton()
button.setTitle("Terms of Service", for: [])
button.setTitleColor(.secondaryLabel, for: [])
return button
}()
private let privacyButton: UIButton = {
return UIButton()
}()
private let createAccountButton: UIButton = {
let button = UIButton()
button.setTitleColor(.lightGray, for: [])
button.setTitle("New User? Create an Account", for: [])
button.backgroundColor = .systemBlue
button.layer.cornerRadius = Constants.cornerRadius
button.setTitleColor(.label, for: .normal)
return button
}()
//Picture behind the Logo
private let headerView: UIView = {
let header = UIView()
header.clipsToBounds = true
let backgroundImageView = UIImageView(image: UIImage(named: "Bluewater"))
header.addSubview(backgroundImageView)
return header
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//assigning frames
headerView.frame = CGRect(x: 0,
y: 0.0,
width: view.width,
height: view.height/3.0
)
usernameEmailField.frame = CGRect(x: 25,
y: headerView.bottom + 10,
width: view.width-50,
height: 52.0
)
passwordField.frame = CGRect(x: 25,
y: usernameEmailField.bottom + 10,
width: view.width-50,
height: 52.0
)
loginButton.frame = CGRect(x: 25,
y: passwordField.bottom + 10,
width: view.width-50,
height: 52.0
)
createAccountButton.frame = CGRect(x: 25,
y: loginButton.bottom + 10,
width: view.width-50,
height: 52.0
)
termsButton.frame = CGRect(x: 10, y: view.height-view.safeAreaInsets.bottom-100, width: view.width-20, height: 50)
configurationView()
}
private func configurationView() {
guard headerView.subviews.count == 1 else {
return
}
guard let backgroundView = headerView.subviews.first else {
return
}
backgroundView.frame = headerView.bounds
//Pic Pool Logo
let imageView = UIImageView(image: UIImage(named: "ClearLogo"))
headerView.addSubview(imageView)
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: headerView.width/4.0,
y: view.safeAreaInsets.top,
width: headerView.width/2.0,
height: headerView.height - view.safeAreaInsets.top)
}
private func addSubviews(){
view.addSubview(usernameEmailField)
view.addSubview(passwordField)
view.addSubview(loginButton)
view.addSubview(termsButton)
view.addSubview(privacyButton)
view.addSubview(createAccountButton)
view.addSubview(headerView)
}
#objc private func didTaploginButton() {}
#objc private func didTaptermsButton() {}
#objc private func didTapprivacyButton() {}
#objc private func didTapcreateAccountButton() {}
}
extension UIView {
var width: CGFloat {
return bounds.width
}
var height: CGFloat {
return bounds.height
}
var bottom: CGFloat {
return frame.minY + bounds.height
}
}
I'd also, strongly, recommend either making the time to learn how to use auto layout OR SwiftUI instead
An array of UIButton are generated programmatically. Is it possible to get the titleLabel of the UIButton triggering the drag? Or are there any ways to get info of the UIButton in the drag function?
override func viewDidLoad() {
super.viewDidLoad()
for q in question{
addButton(title:q)
}
}
func addButton(title:String){
var tbutton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0,
width: buttonWidth,
height: buttonHeight))
button.center = self.view.center
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
button.backgroundColor = UIColor.yellow
button.setTitleColor(.black, for: .normal)
button.setTitle(title, for: .normal)
return button
}()
view.addSubview(tbutton)
tbutton.addTarget(self,
action: #selector(drag(control:event:)),
for: UIControl.Event.touchDragInside)
tbutton.addTarget(self,
action: #selector(drag(control:event:)),
for: [UIControl.Event.touchDragExit,
UIControl.Event.touchDragOutside])
self.buttonArray.append(tbutton)
}
#objc func drag(control: UIControl, event: UIEvent) {
//print(event)
if let center = event.allTouches?.first?.location(in: self.view) {
control.center = center
}
/////////////////////////////////////////
// Get titleLabel of the button here???????????
/////////////////////////////////////////
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = .yellow
button.center = CGPoint.init(x: view.bounds.width / 2, y: view.bounds.height / 2)
view.addSubview(button)
let pangesture = UIPanGestureRecognizer.init(target: self, action: #selector(ViewController.panning(_:)))
button.addGestureRecognizer(pangesture)
}
#objc func panning(_ panGesture : UIPanGestureRecognizer){
let button = panGesture.view as! UIButton
switch panGesture.state{
case .changed:
button.center = panGesture.location(in: view)
panGesture.setTranslation(.zero, in: view)
default:
break
}
// here you can get the button's properties too.
}
}
I am trying to change the size of some icons in my navBar, but I am a little confused as to how to do this? My code so far is:
func setUpNavBarButtons() {
let moreButton = UIBarButtonItem (image: UIImage(named:"ic_more_vert_3")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMore))
navigationItem.rightBarButtonItems = [moreButton]
let refreshButton = UIBarButtonItem (image: UIImage(named:"ic_refresh")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(refreshDataButton))
navigationItem.leftBarButtonItems = [refreshButton]
}
any help?
This is how I did it
iOS 10 and below
func setUpMenuButton(){
let menuBtn = UIButton(type: .custom)
menuBtn.frame = CGRect(x: 0.0, y: 0.0, width: 20, height: 20)
menuBtn.setImage(UIImage(named:"menuIcon"), for: .normal)
menuBtn.addTarget(self, action: #selector(vc.onMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
let menuBarItem = UIBarButtonItem(customView: menuBtn)
self.navigationItem.leftBarButtonItem = menuBarItem
}
iOS 11 - Navigation bar come up with Autolayout so frame setting may not work
func setUpMenuButton(){
let menuBtn = UIButton(type: .custom)
menuBtn.frame = CGRect(x: 0.0, y: 0.0, width: 20, height: 20)
menuBtn.setImage(UIImage(named:"menuIcon"), for: .normal)
menuBtn.addTarget(self, action: #selector(vc.onMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
let menuBarItem = UIBarButtonItem(customView: menuBtn)
let currWidth = menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 24)
currWidth?.isActive = true
let currHeight = menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 24)
currHeight?.isActive = true
self.navigationItem.leftBarButtonItem = menuBarItem
}
Extension for Swift 4.2
Just a different way of implementation the answer provided by anoop4real
However using button type .system allows the global tint to be applied to your icon
Usage:
navigationItem.leftBarButtonItem = UIBarButtonItem.menuButton(self, action: #selector(presentSettings), imageName: "settings")
Implementation:
extension UIBarButtonItem {
static func menuButton(_ target: Any?, action: Selector, imageName: String) -> UIBarButtonItem {
let button = UIButton(type: .system)
button.setImage(UIImage(named: imageName), for: .normal)
button.addTarget(target, action: action, for: .touchUpInside)
let menuBarItem = UIBarButtonItem(customView: button)
menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 24).isActive = true
menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 24).isActive = true
return menuBarItem
}
}
You can configure the frame of you button like below:
let icon = UIImage(named: "imageName")
let iconSize = CGRect(origin: CGPoint.zero, size: CGSize(width: 50, height: 50))
let iconButton = UIButton(frame: iconSize)
iconButton.setBackgroundImage(icon, for: .normal)
let barButton = UIBarButtonItem(customView: iconButton)
iconButton.addTarget(self, action: #selector(foo), for: .touchUpInside)
navigationItem.leftBarButtonItem = barButton
#DogCoffee answer is a great and creative way to solve the problem. May I suggest some slightly mods in order to take into account size and tintColor
extension UIBarButtonItem {
static func menuButton(_ target: Any?,
action: Selector,
imageName: String,
size:CGSize = CGSize(width: 32, height: 32),
tintColor:UIColor?) -> UIBarButtonItem
{
let button = UIButton(type: .system)
button.tintColor = tintColor
button.setImage(UIImage(named: imageName), for: .normal)
button.addTarget(target, action: action, for: .touchUpInside)
let menuBarItem = UIBarButtonItem(customView: button)
menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
menuBarItem.customView?.heightAnchor.constraint(equalToConstant: size.height).isActive = true
menuBarItem.customView?.widthAnchor.constraint(equalToConstant: size.width).isActive = true
return menuBarItem
}
}
In the end I did it like this and it worked:
let moreButton = UIButton(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
moreButton.setBackgroundImage(UIImage(named: "ic_more_vert_3"), for: .normal)
moreButton.addTarget(self, action: #selector(TableViewController.handleMore), for: .touchUpInside)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: moreButton)
Answer from: Change width of a UIBarButtonItem in a UINavigationBar in swift
If you are using SF symbol image, which is available from iOS 13, you could set the SF symbol image configuration.
let config = UIImage.SymbolConfiguration(pointSize: 30, weight: .light, scale: .default)
let image = UIImage(systemName: "plus.circle", withConfiguration: config)
Swift 5 extension using button closure, expanding on #valvoline 's answer.
As you can appreciate instead of using target and selector Button allows you to pass a closure.
extension UIBarButtonItem {
static func imageButton(
image: UIImage,
size: CGFloat = 24,
color: UIColor = .systemBackground,
action: #escaping () -> Void
) -> UIBarButtonItem {
// create ui button
let button = UIButton(type: .system)
// assign image
button.setImage(image, for: .normal)
button.tintColor = color
// assign action
button.addAction(.init(handler: { _ in action() }), for: .touchUpInside)
// create menu bar item using custom view
let menuBarItem = UIBarButtonItem(customView: button)
//use auto layout to assign bar button's size
menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
[
menuBarItem.customView?.heightAnchor.constraint(equalToConstant: size),
menuBarItem.customView?.widthAnchor.constraint(equalToConstant: size)
].compactMap { $0 }
)
return menuBarItem
}
}
Usage Example:
let item = UIBarButtonItem.imageButton(image: image, color: .systemBlue) {
print("You pushed it!") }
You can configure the bar buttons using this function:
public convenience init(customView: UIView)
And You can init the custom view as You desire.
After that You can access the view if needed via UIBarButtonItem's:
open var customView: UIView?
Hint: Because UIButton is a child class of UIView, You can directly use it too.
Here is my code. I get an unrecognized selector sent to instance crash/error every time I tap addButton. I've looked for the past hour for an answer and can't find one. Please help.
#IBOutlet var addButton: UIButton!
#IBOutlet var resetButton: UIButton!
var number:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
let size: CGSize = self.view.frame.size;
countLabel.frame = CGRect(x: 0, y:0, width: size.width, height: size.height/4)
minusButton.frame = CGRect(x:0, y: size.height/4, width: size.width/2, height: size.height/8);
addButton.frame = CGRect(x:0, y:size.height/4 + size.height/8, width: size.width, height: 5*size.height/8)
resetButton.frame = CGRect(x:size.width/2, y: size.height/4, width: size.width/2 ,height: size.height/8)
addButton.addTarget(self, action: #selector(addNumber(sender:)), for: UIControlEvents.touchUpInside)
minusButton.addTarget(self, action: #selector(minusNumber), for: UIControlEvents.touchUpInside)
resetButton.addTarget(self, action: #selector(resetNumber), for: UIControlEvents.touchUpInside)
}
func addNumber(sender: UIButton!) {
number = number + 1
countLabel.text = String(number)
}
Change
addButton.addTarget(self, action: #selector(addNumber(sender:)), for: UIControlEvents.touchUpInside)
With
addButton.addTarget(self, action: #selector(addNumber), for: UIControlEvents.touchUpInside)
You don't need to include sender in a selector.
So you need to change your code to
addButton.addTarget(self, action: #selector(addNumber), for: .touchUpInside).
And you don't need to implicitly unwrap sender
func addNumber(sender: UIButton)
Add #objc keyword before selector declaration:
#objc func addNumber(sender: UIButton!) {
number = number + 1
countLabel.text = String(number)
}
I have the following buttons which are adding dynamically based on some condition. Now i need to change all added buttons behaviour as radio buttons. so that I can do some actions based on the selection..
var posX = 0
var posY = 0
if trim.contains("0"){
print("contaim 0")
let button1 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button1.setTitleColor(UIColor.blackColor(), forState: .Normal)
button1.setTitle("No", forState: .Normal)
button1.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button1.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button1)
posX = 60
}
if trim.contains("1") {
print("contaim 1")
let button2 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button2.setTitleColor(UIColor.blackColor(), forState: .Normal)
button2.setTitle("Less", forState: .Normal)
button2.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button2.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button2)
posX = posX + 60
}
if trim.contains("2"){
print("contaim 2")
let button3 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button3.setTitleColor(UIColor.blackColor(), forState: .Normal)
button3.setTitle("Half", forState: .Normal)
button3.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button3.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button3)
posX = posX + 60
}
I have set the buttonAction methods as below buts its not working
func buttonAction(sender: UIButton!) {
print("Button tapped")
sender.setImage(UIImage(named: "checkboxredtick.png")!, forState: .Normal)
}
let buttons = [UIButton]()
// create button1
let button1 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button1.setTitleColor(UIColor.blackColor(), forState: .Normal)
button1.setTitle("No", forState: .Normal)
button1.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
// if the selected button cannot be reclick again, you can use .Disabled state
button1.setImage(UIImage(named: "checkboxredtick.png")!, forState: .Selected)
button1.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button1)
buttons.append(button1)
// create other buttons and add into buttons ...
func buttonAction(sender: UIButton!){
for button in buttons {
button.selected = false
}
sender.selected = true
// you may need to know which button to trigger some action
// let buttonIndex = buttons.indexOf(sender)
}
In case anyone finds this useful. Here is an example of how I use DLRadioButton library (https://github.com/DavydLiu/DLRadioButton) to create RadioButtons dynamically.
You can check a complete small project to show a working code in here https://github.com/omhack/DynamicRadioButtons
import UIKit
import DLRadioButton
//Delegate to manage the Polls
protocol QuestionsDialogDelegate : class{
func questionsAnswered(polls: [Poll])
}
class QuestionsDialog: UIViewController {
#IBOutlet weak var stackView: UIStackView!
#IBOutlet var rootView: UIView!
#IBOutlet weak var nestedView: UIView!
weak var delegate: QuestionsDialogDelegate?
var questions : [Question]!
var polls = [Poll]()
var serviceType : Int = 0
var indexView : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
//setting the stack view properties
stackView.alignment = UIStackViewAlignment.leading
stackView.axis = .vertical
}
//this is where the heavy logic, to create the dynamic radio buttons takes place
func showQuestions(){
if(questions.count <= 1){
rootView.frame.size.height = 200
nestedView.frame.size.height = 200
}else{
rootView.frame.size.height = 400
nestedView.frame.size.height = 400
}
for question in questions{
var otherButtons : [DLRadioButton] = []
let frame1 = CGRect(x: self.view.frame.size.width / 2 - 131, y: 350, width: 262, height: 17);
//we create a base radio button to use it as an anchor view
let firstRadioButton = createRadioButton(frame: frame1, title: "", color: UIColor.purple);
let label = UILabel()
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 17.0)
label.textColor = UIColor.darkGray.withAlphaComponent(0.85)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.25
label.frame = CGRect(x: 0, y: 0, width: 200, height: 30)
label.text = question.question
self.stackView.insertArrangedSubview(label, at: self.indexView)
self.indexView += 1
let poll = Poll()
poll.idQuestion = question.idQuestion
var i = 0;
for answer in question.answers{
let frame = CGRect(x: self.view.frame.size.width / 2 - 131, y: 380 + 30 * CGFloat(i), width: 300, height: 17);
let radioButton = createRadioButton(frame: frame, title: answer.answer! + " ", color: UIColor.purple)
radioButton.tag = answer.idAnswer
radioButton.params["poll"] = poll
otherButtons.append(radioButton)
self.stackView.insertArrangedSubview(radioButton, at: self.indexView)
i += 1;
self.indexView += 1
}
firstRadioButton.otherButtons = otherButtons
firstRadioButton.isHidden = true
firstRadioButton.otherButtons[1].isSelected = true
}
}
//Method to create a custom button
private func createRadioButton(frame : CGRect, title : String, color : UIColor) -> MyDLUIButton {
let radioButton = MyDLUIButton(frame: frame);
radioButton.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
radioButton.titleLabel!.font = UIFont.systemFont(ofSize: 14);
radioButton.setTitle(title, for: []);
radioButton.setTitleColor(UIColor.darkGray, for: []);
radioButton.iconColor = color;
radioButton.indicatorColor = color;
radioButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;
radioButton.addTarget(self, action: #selector(QuestionsDialog.selectedAnswer(_:)), for: UIControlEvents.touchUpInside)
self.view.addSubview(radioButton);
return radioButton;
}
#objc func selectedAnswer(_ sender: MyDLUIButton){
let poll = sender.params["poll"] as? Poll
poll?.idAnswer = sender.tag
if let row = self.polls.index(where: {$0.idQuestion == poll?.idQuestion}) {
self.polls[row] = poll!
}else{
self.polls.append(poll!)
}
print("polls size: \(self.polls.count)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidDisappear(_ animated: Bool) {
if(self.polls.count < self.questions.count){
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "questionsDialogDismissed"), object: nil)
}
}
#IBAction func requestService(_ sender: UIButton) {
delegate?.questionsAnswered(polls: self.polls)
}
}
class MyDLUIButton: DLRadioButton{
var params: Dictionary<String, Any>
override init(frame: CGRect) {
self.params = [:]
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
self.params = [:]
super.init(coder: aDecoder)
}
}
I would create a tag for every UIButton, then add your UIButton to be send to your ButtonAction:
button.tag = 0
button.addTarget(object, action: #selector(YourViewController.buttonAction(_:)),
forControlEvents: .TouchUpInside)
func buttonAction(sender: UIButton!) {
let selectedButton = sender as! UIButton
print(selectedButton.tag) // here stands the correct button which is pressed
}
You could also create an Array containing all Buttons, for example:
let myButtons = [UIButton]()
let button1 = UIButton()...
button1.tag = 0
myButtons.add(button1)
And now change it like this way:
myButtons[0].setImage(.....)
Where 0 is your first button (so with tag 0) // you should set your tags in the same order in which you append it to your myButtons Array.