I need to be able to tap out of the keyboard when not tapping in uitextfield or when not tapping show/hide password button.
I was previously using this code to do that:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
But the problem with that was that it clicks out of the keyboard even when clicking the show/hide password eye icon. The code I'm using for the show/hide icon is this:
extension UITextField {
func showhidepasswordbutton(image: UIImage = UIImage(systemName: "eye.slash")!) {
let button = UIButton(type: .custom)
button.setImage(image, for: .normal)
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.refreshforshowhide), for: .touchUpInside)
button.tintColor = .darkGray
self.rightView = button
self.rightViewMode = .always
}
#IBAction func refreshforshowhide(_ sender: Any) {
print("ok")
if self.isSecureTextEntry == true {
self.togglePasswordVisibility()
showhidepasswordbutton(image: UIImage(systemName: "eye")!)
} else if self.isSecureTextEntry == false {
self.togglePasswordVisibility()
showhidepasswordbutton(image: UIImage(systemName: "eye.slash")!)
}
}
func togglePasswordVisibility() {
let temptext = self.text
isSecureTextEntry.toggle()
self.text = ""
self.text = temptext
}
}
Sorry for the messy code, just wrote the show/hide password code up.
You could exclude the taps on subviews using gestureRecognizer(_:, shouldReceive:) method in UIGestureRecognizerDelegate.
extension UIViewController: UIGestureRecognizerDelegate {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
tap.delegate = self
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
touch.view?.isDescendant(of: view) == false // will return false if touch was received by a subview
}
}
Update: You could use touch.view == view instead of touch.view?.isDescendant(of: view) == false.
Related
I have a custom keyboard project with a Storyboard containing a collectionView and a scrollView. I also have a textField for a search bar that pulls up a nib containing a keyboard layout that I can use to search within the keyboard. When the subview containing the keyboard becomes active, I cannot press any of the buttons. I have tried bringing the subView to front upon becoming active and have tried sending the main view to the back when hiding the collectionView. What can I do to use the buttons in the subview?
Before viewDidLoad
let filename = "buttonClick"
let ext = "m4a"
var soundId: SystemSoundID = 0
#objc func typeKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
self.key.txtSearch.text = "\(self.key.txtSearch.text!)\((sender.titleLabel?.text!)!)"
if(self.key.txtSearch.text != "")
{
isCaps = false
updateKeyBoard()
}
}
#objc func typeSpaceKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
self.key.txtSearch.text = "\(self.key.txtSearch.text!) "
}
#objc func typeDoneKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
hideTextField()
}
#objc func typeNumKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
isNum = !isNum
updateKeyBoard()
// self.key.txtSearch.text = "\(self.key.txtSearch.text!)\((sender.titleLabel?.text!)!)"
}
#objc func typeBackSpaceKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
self.key.txtSearch.text = "\(self.key.txtSearch.text!.dropLast())"
if(self.key.txtSearch.text == "")
{
isCaps = true
updateKeyBoard()
}
}
#objc func typeCapsKey(sender : UIButton)
{
AudioServicesPlaySystemSound(soundId)
if(isNum)
{
isFirst = !isFirst
}
else
{
isCaps = !isCaps
}
updateKeyBoard()
}
#objc func updateKeyBoard()
{
if(isCaps)
{
self.key.btnCap.setImage(#imageLiteral(resourceName: "cap_F"), for: .normal)
}
else
{
self.key.btnCap.setImage(#imageLiteral(resourceName: "caps"), for: .normal)
}
var count = 0
for btn in buttons
{
if(!isNum)
{
if(isCaps)
{
btn.setTitle("\(arrCapOn[count])", for: .normal)
}
else
{
btn.setTitle("\(arrCapOff[count])", for: .normal)
}
}
else
{
if(isFirst)
{
btn.setTitle("\(arrNumCapOn[count])", for: .normal)
}
else
{
btn.setTitle("\(arrNumCapOff[count])", for: .normal)
}
}
count = count + 1
}
}
#objc func activeTextField()
{
self.key.btnBack.isHidden = false
self.key.txtSearch.becomeFirstResponder()
self.key.constLeftAchor.constant = 40
self.key.constSideKeyboard.constant = 8
self.key.constLeftAchorView.constant = 32
UIView.animate(withDuration: 0.2) {
self.key.viewKeyboard.transform = CGAffineTransform(translationX: 0, y: 0)
self.key.btnBack.alpha = 1.0
self.key.layoutIfNeeded()
}
self.key.btnTextFieldSelect.isHidden = true
}
#objc func hideTextField()
{
self.key.btnBack.isHidden = true
self.key.txtSearch.resignFirstResponder()
self.key.btnTextFieldSelect.isHidden = false
self.key.constSideKeyboard.constant = 400
self.key.constLeftAchor.constant = 24
self.key.constLeftAchorView.constant = 16
UIView.animate(withDuration: 0.2) {
self.key.viewKeyboard.transform = CGAffineTransform(translationX: self.view.frame.width, y: 0)
self.key.btnBack.alpha = 0.0
self.key.layoutIfNeeded()
}
}
#objc func hideArticles(){
self.categoriesScrollView.isHidden = true
self.collectionview.isHidden = true
//self.collectionview.isUserInteractionEnabled = false
//self.view.bringSubview(toFront: key)
//self.key.isUserInteractionEnabled = true
}
#objc func showArticles(){
self.categoriesScrollView.isHidden = false
self.collectionview.isHidden = false
}
#objc func handleTap(_ sender: UITapGestureRecognizer) {
self.key.txtSearch.text = "Hello"
self.inputView?.resignFirstResponder()
print("Hello World")
}
func textFieldDidBeginEditing(_ textField: UITextField) {
}
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.
var textColor: UIColor
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
textColor = UIColor.white
} else {
textColor = UIColor.black
}
// self.nextKeyboardButton.setTitleColor(textColor, for: [])
}
after viewDidLoad
if let soundUrl = Bundle.main.url(forResource: filename, withExtension: ext) {
AudioServicesCreateSystemSoundID(soundUrl as CFURL, &soundId)
}
self.key = Bundle.main.loadNibNamed("keyboard", owner: nil, options: nil)![0] as! keyboard
self.key.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 160)
//self.key.txtSearch.isUserInteractionEnabled = false
self.key.constLeftAchor.constant = 24
// self.key.viewKeyboard.transform = CGAffineTransform(translationX: self.view.frame.width, y: 0)
self.key.btnBack.isHidden = true
self.key.btnBack.alpha = 0.0
self.key.btnTextFieldSelect.addTarget(self, action: #selector(activeTextField), for: .touchUpInside)
self.key.btnTextFieldSelect.addTarget(self, action: #selector(hideArticles), for: .touchUpInside)
self.key.btnBack.addTarget(self, action: #selector(hideTextField), for: .touchUpInside)
self.key.btnBack.addTarget(self, action: #selector(showArticles), for: .touchUpInside)
buttons.append(self.key.btnQ)
buttons.append(self.key.btnW)
buttons.append(self.key.btnE)
buttons.append(self.key.btnR)
buttons.append(self.key.btnT)
buttons.append(self.key.btnY)
buttons.append(self.key.btnU)
buttons.append(self.key.btnI)
buttons.append(self.key.btnO)
buttons.append(self.key.btnP)
buttons.append(self.key.btnA)
buttons.append(self.key.btnS)
buttons.append(self.key.btnD)
buttons.append(self.key.btnF)
buttons.append(self.key.btnG)
buttons.append(self.key.btnH)
buttons.append(self.key.btnJ)
buttons.append(self.key.btnK)
buttons.append(self.key.btnL)
buttons.append(self.key.btnZ)
buttons.append(self.key.btnX)
buttons.append(self.key.btnC)
buttons.append(self.key.btnV)
buttons.append(self.key.btnB)
buttons.append(self.key.btnN)
buttons.append(self.key.btnM)
for btn in buttons
{
btn.addTarget(self, action: #selector(typeKey), for: .touchUpInside)
}
self.key.btnCap.addTarget(self, action: #selector(typeCapsKey), for: .touchUpInside)
self.key.btnBackSpace.addTarget(self, action: #selector(typeBackSpaceKey), for: .touchUpInside)
self.key.btnSpace.addTarget(self, action: #selector(typeSpaceKey), for: .touchUpInside)
self.key.btnDone.addTarget(self, action: #selector(typeDoneKey), for: .touchUpInside)
//self.key.btnDone.addTarget(self, action: #selector(fetchSearch), for: .touchUpInside)
self.key.btnNum.addTarget(self, action: #selector(typeNumKey), for: .touchUpInside)
view.addSubview(key)
activeTextField()
hideTextField()
Please make sure the added subviews are userinteration enabled and it is not send to back in superview.
I would like to change the border color of a uiview when it's being pressed and return to the normal border color after release. What is the best practise for doing this?
Something like the picture below:
Don't use UITapGestureRecognizer. You can use UILongPressGestureRecognizer
Code:
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView.init(frame: CGRect.init(x: 30, y: 200, width: 100, height: 40))
self.view.addSubview(view)
view.layer.borderColor = UIColor.black.cgColor
view.layer.borderWidth = 3
let tapForView = UILongPressGestureRecognizer(target: self, action: #selector(self.toChangeColor(recognizer:)))
tapForView.minimumPressDuration = 0.01
view.isUserInteractionEnabled = true
view.addGestureRecognizer(tapForView)
}
#objc func toChangeColor(recognizer:UILongPressGestureRecognizer)
{
// Apply logic for changing background color.
let view = recognizer.view
if recognizer.state == .began {
view?.layer.borderColor = UIColor.orange.cgColor
print("view began")
}
else if recognizer.state == .ended {
view?.layer.borderColor = UIColor.black.cgColor
print("view ended")
}
}
This will work perfectly.
You can use UILongPressGestureRecognizer to change border color of UIView
Add Gesture in viewDidLoad and in handleTap function you can change border color.
override func viewDidLoad() {
super.viewDidLoad()
let tap = UILongPressGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
customeview.addGestureRecognizer(tap)
}
#objc func handleTap(_ sender: UILongPressGestureRecognizer) {
print("Hello World")
if sender.state == .began
{
customeview.layer.borderColor = UIColor.yellow.cgColor
customeview.layer.borderWidth = 3
}
else if sender.state == .ended
{
customeview.layer.borderColor = UIColor.black.cgColor
customeview.layer.borderWidth = 3
}
}
let tapForView = UITapGestureRecognizer(target: self, action: #selector(self.ToChangeColor(recognizer:)))
tapForView.numberOfTapsRequired = 1
view.isUserInteractionEnabled = true
view.addGestureRecognizer(tapForView)
#objc func ToChangeColor(recognizer:UITapGestureRecognizer)
{
// Apply logic for changing background color
}
I managed to show a custom clearbutton, the problem is that it will not be removed when clicking anywhere else or clicking other textfield. It is always showing.
Here is my extension:
extension UITextField {
func clearButtonWithImage(_ image: UIImage) {
let clearButton = UIButton()
clearButton.setImage(image, for: .normal)
clearButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
clearButton.contentMode = .scaleAspectFit
clearButton.addTarget(self, action: #selector(self.clear(sender:)), for: .touchUpInside)
self.rightView = clearButton
self.rightViewMode = .always
}
func clear(sender: AnyObject) {
self.text = ""
}
}
and here i show the clearbutton on the method:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == dateSearchTextField {
self.performSegue(withIdentifier: "showCalendar", sender: self)
textField.clearButtonWithImage(#imageLiteral(resourceName: "icClear"))
return false
} else if textField == timeSearchTextField {
self.performSegue(withIdentifier: "showTimePicker", sender: self)
textField.clearButtonWithImage(#imageLiteral(resourceName: "icClear"))
return false
}
return true
}
I want it to be visible only when clicking inside the textfield.
Replace this:
self.rightViewMode = .always
With:
self.rightViewMode = .whileEditing
I have a subclass of AVPlayerViewController that I want to add my custom controls on it. I successfully added my custom controls on top of the AVPlayerViewController. However, these controls are only responsive for a short time. After a few seconds, clicking any of the button won't trigger the touchUpInside event.
Below is the code I'm using:
class LSPlayerViewController : AVPlayerViewController {
private var btnPlay : UIButton!
func addControls() {
self.showsPlaybackControls = false
//PLAY BUTTON
let btnPlayRect = CGRect(x: 0, y: 0, width: 19, height: 25)
btnPlay = UIButton(frame: btnPlayRect)
btnPlay.center = self.view.center
btnPlay.setImage(#imageLiteral(resourceName: "PauseIcon"), for: .normal)
btnPlay.addTarget(self, action: #selector(self.playButtonTapped(sender:)), for: .touchUpInside)
btnPlay.alpha = 0
btnPlay.isUserInteractionEnabled = true
self.view.addSubview(btnPlay)
}
func playButtonTapped(sender: UIButton!) {
if sender.isPlayIconOn == true {
sender.setImage(#imageLiteral(resourceName: "PauseIcon"), for: .normal)
self.player?.play()
} else {
sender.setImage(#imageLiteral(resourceName: "PlayIcon"), for: .normal)
self.player?.pause()
}
}
I solved the issue by adding the following two lines alongside the addSubview function:
self.addChildViewController(playerViewController)
playerView.addSubview(playerViewController.view)
playerViewController.didMove(toParentViewController: self)
I'd like my button to remain highlighted after the user taps it. If the user taps the button again I'd like it to become de-selected/unhighlighted. I'm not sure how to go about doing this in swift. I'm currently setting the button highlight image and selected image to the same .png using interface builder.
When I run the app and tap the button, it changes to my highlight image for as long as my finger remains on the button.
Use below code
declare isHighLighted as instance variable
//write this in your class
var isHighLighted:Bool = false
override func viewDidLoad() {
let button = UIButton(type: .system)
button.setTitle("Your title", forState: UIControlState.Normal)
button.frame = CGRectMake(0, 0, 100, 44)
self.view.addSubview(button as UIView)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton)
{
dispatch_async(dispatch_get_main_queue(), {
if isHighLighted == false{
sender.highlighted = true;
isHighLighted = true
}else{
sender.highlighted = false;
isHighLighted = false
}
});
}
I would recomend to use selected state instead of highlighted the below code demonstarate with selected state
override func viewDidLoad() {
let button = UIButton(type: .system)
button.setTitle("Your title", forState: UIControlState.Normal)
button.frame = CGRectMake(0, 0, 100, 44)
self.view.addSubview(button as UIView)
//set normal image
button.setImage(normalImage, forState: UIControlState.Normal)
//set highlighted image
button.setImage(selectedImage, forState: UIControlState.Selected)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton)
{
sender.selected = !sender.selected;
}
func buttonPressed(_ sender: UIButton) {
// "button" is a property
if button.isSelected {
button.setImage(UIImage(named: "filled-heart"), for: .normal)
button.isSelected = false
}else {
button.setImage(UIImage(named: "empty-heart"), for: .selected)
button.isSelected = true
}
}
func highlightButton(button: UIButton) {
button.highlighted = true
}
#IBAction func touched(sender: UIButton) {
let timer = NSTimer.scheduledTimerWithTimeInterval(0.0, target: self, selector: Selector("highlightButton(sender)"), userInfo: nil, repeats: true)
}
this one worked fine for me!
func buttonColorChanger(sender : UIButton ) {
if button.isSelected == false
{
button.backgroundColor = UIColor.purple
print("selected")
button.setTitle("selected", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.isSelected = true
}else{
button.backgroundColor = UIColor.white
print("unselected")
button.isSelected = false
}
}
Swift 5:
#IBAction func toggleButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
}