Add Gesture Recogniser - swift

For some reason the gesture recogniser does not work, it doesn't make the background normal again. How do I fix it so the selector (self.dissmissMenu) works?
In View controller:
let menu = Menu()
#IBAction func menuButton(sender: AnyObject) {
menu.runMenu()
}
In Menu:
import UIKit
class Menu: NSObject {
let dimming = UIView()
public func runMenu(){
if let window = UIApplication.sharedApplication().keyWindow{
dimming.frame = window.frame
dimming.backgroundColor = UIColor(white: 0, alpha: 0.5)
dimming.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector(self.dissmissMenu())))
window.addSubview(dimming)
UIView.animateWithDuration(0.5, animations: {
self.dimming.alpha = 1
})
}
}
public func dissmissMenu(){
UIView.animateWithDuration(0.5, animations: {
self.dimming.alpha = 0
})
}
}

Try to rewrite the call to addGestureRecognizer like this:
dimming.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dissmissMenu())))

Related

UIButton Animations not working on UIScrollView

I have button animations that are not working on my ScrollView. I have other VCs that are just UIViews and animations work just fine.
So I tried unchecking from the Attributes Inspector and adding...
self.scrollView.delaysContentTouches = false
...in my viewDidLoad, but it had effect.
I also found that tapping does not trigger the animation but holding on the button for a second or more does and haptic presses also trigger the animation.
This is my animation code:
extension UIButton {
func startAnimatingPressActions() {
addTarget(self, action: #selector(animateDown), for: [.touchDown, .touchDragEnter])
addTarget(self, action: #selector(animateUp), for: [.touchDragExit, .touchCancel, .touchUpInside, .touchUpOutside])
}
#objc private func animateDown(sender: UIButton) {
animate(sender, transform: CGAffineTransform.identity.scaledBy(x: 0.95, y: 0.95))
}
#objc private func animateUp(sender: UIButton) {
animate(sender, transform: .identity)
}
private func animate(_ button: UIButton, transform: CGAffineTransform) {
UIView.animate(withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 3,
options: [.curveEaseInOut],
animations: {
button.transform = transform
}, completion: nil)
}
}
I am able to get this animation working, but it doesn't feel quite right. So I'd rather not use the code below. But it does work. So I'm not sure what in the code above is causing the problem.
extension UIButton {
func pulsate() {
let pulse = CASpringAnimation(keyPath: "transform.scale")
pulse.duration = 0.2
pulse.fromValue = 0.96
pulse.toValue = 1.0
pulse.repeatCount = 0
pulse.initialVelocity = 0.5
pulse.damping = 1.0
layer.add(pulse, forKey: nil)
}
}
I had the same issue and I solved unchecking the delay touch down button in the scrollview's attributes inspector :
ScrollView's attrobutes inspector

UIView alpha does not update after first call to change it. (Swift 4.2)

I have a UIView on which I want to change the alpha from 0 to 0.5 when the user opens a slide out menu. When the user taps the darkened area the alpha should go back to 0. Currently, when the menu button is tapped the alpha changes to 0.5 adding a dimming effect to the view. However, a breakpoint and print statement show that when tapping the UIView the line to change the alpha back to 0 runs, but the UI still shows a 0.5 alpha. Everywhere I have looked the code is exactly the same, so I am unsure what I am doing wrong.
let dimView = UIView()
func setupMenuButton() {
let menuButton = UIBarButtonItem(title: "Menu", style: .plain, target: self, action: #selector(showMenu))
navigationItem.rightBarButtonItem = menuButton
}
#objc func showMenu() {
//TODO: present menu and dim background
if let window = UIApplication.shared.keyWindow {
let dimView = UIView()
dimView.backgroundColor = UIColor.black
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissDimView)))
window.addSubview(dimView)
dimView.frame = window.frame
dimView.alpha = 0
UIView.animate(withDuration: 0.5, animations: {
dimView.alpha = 0.5
})
}
}
#objc func dismissDimView() {
UIView.animate(withDuration: 0.5, animations: {
self.dimView.alpha = 0
print("dim view is not transparent")
})
}
The dimView created in showMenu is not the same dimView created in the first line. You are creating a brand new dimView in showMenu.
One way to fix this is to not create a new dimView in showMenu, and use the one declared outside instead:
#objc func showMenu() {
//TODO: present menu and dim background
if let window = UIApplication.shared.keyWindow {
// notice I deleted a line here
dimView.backgroundColor = UIColor.black
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissDimView)))
window.addSubview(dimView)
dimView.frame = window.frame
dimView.alpha = 0
UIView.animate(withDuration: 0.5, animations: {
dimView.alpha = 0.5
})
}
}
#objc func dismissDimView() {
UIView.animate(withDuration: 0.5, animations: {
self.dimView.alpha = 0
// here I remove the dimView from the window so that it can be added back in the next time showMenu is called
}, completion: { [weak self] _ in self?.dimView.removeFromSuperView() })
}

Strange animation 'lag' after launch screen disappears and before viewDidAppear() method runs

After launch screen dismisses itself logo and title of my app (they are in container) should go closer to the top of the screen. Between dismissing launch screen and viewDidAppear method there is a strange 'blink' of my container in the background. As you can see I am using snapkit but it should have nothing to do with the problem. Here is my code:
class WelcomeScreenViewController: UIViewController {
var welcomeScreenView: WelcomeScreenView {
return view as! WelcomeScreenView
}
override func loadView() {
let contentView = WelcomeScreenView(frame: .zero)
view = contentView
}
override func viewDidLoad() {
super.viewDidLoad()
self.welcomeScreenView.checkWeatherButton.transform = CGAffineTransform(translationX: 0, y: 200)
self.welcomeScreenView.checkWeatherButton.addTarget(self, action: #selector(showCityChoiceVC), for: .touchUpInside)
navigationController?.isNavigationBarHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.welcomeScreenView.appNameLogoContainerVerticalConstraint?.isActive = false
self.welcomeScreenView.appNameLogoContainer.snp.makeConstraints({ (make) in
make.top.equalTo(self.welcomeScreenView).offset(100)
})
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: {
self.welcomeScreenView.layoutIfNeeded()
self.welcomeScreenView.checkWeatherButton.transform = CGAffineTransform(translationX: 0, y: 0)
}, completion: nil)
}
#objc private func showCityChoiceVC() {
self.navigationController?.pushViewController(RegisterViewController(), animated: true)
}
Blinking comes from setting constraints in viewDidAppear. Use viewWillAppear or viewDidLoad instead. viewDidAppear is invoked when your view actually appears on screen. So any changes that happen will be visible to the user.

Could not execute a function on UITapGestureRecognizer

Whenever i touch on the screen nothing happens. looks like handleDismiss() function is never fired. Does anybody knows what is the real problem here.
import UIKit
class SettingLauncher : NSObject{
let blackView = UIView();
func showSettingMenu(){
if let window = UIApplication.shared.keyWindow{
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5);
blackView.isUserInteractionEnabled = true;
blackView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: #selector(self.handleDismiss)));
window.addSubview(blackView);
blackView.frame = window.frame;
blackView.alpha = 0.1;
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0.5;
}
}
}
func handleDismiss(){
print("Touch recognised");
}
}
You have to tell the UITapGestureRecognizer which class will be handling the response - not just which function, but which class
Change
blackView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: #selector(self.handleDismiss)));
To
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleDismiss)))
Here's an example which I have working. I am initiating the view from a button - I don't know how yours is started, but that shouldn't matter
class ViewController: UIViewController
{
let blackView = UIView()
override func viewDidLoad()
{
super.viewDidLoad()
}
func handleDismiss(){
print("Touch recognised");
}
#IBAction func cmdDoOtherStuff(_ sender: Any)
{
if let window = UIApplication.shared.keyWindow{
blackView.backgroundColor = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.5)//(r: 0, alpha: 0.5)
blackView.isUserInteractionEnabled = true
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleDismiss)))
window.addSubview(blackView)
blackView.frame = window.frame
blackView.alpha = 0.1
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0.5;
}
}
}
}
I got the above error because I called the function showSettingMenu() through another class function as below:-
func handleMoreSetting(){
let settingLauncher = SettingLauncher();
settingLauncher.showSettingMenu(); // Call SettingLauncher class
}
The problem went away when I declared settingLauncher as global instance like below.
let settingLauncher = SettingLauncher();
func handleMoreSetting(){
settingLauncher.showSettingMenu(); // Call SettingLauncher class
}
Do anybody knows why the problem occured? In both cases, I am calling the same function but with a different result.

Switching between UITextViews resets view Layout

I implemented some functions to move the UIView, whenever a TextField or a TextView is tapped. The height for the UIView to move upwards is calculated depending how much the keyboard would overlap the active TextField or TextView. When I tap outside of the TextField, or -View the keyboard will be dismissed and the View will be resetted. Now everything is working fine, but when I switch from one TextField directly to another above (without dismissing the keyboard) it seems like the UIView will return to the initial position, instead of just keeping the shifted view (because the now active TextView would not be overlapped by the keyboard since it is above the former). It looks like some method is called to reset the view, resulting in the keyboard overlapping the upper TextView. Is there a way to suppress this behavior?
private func observeKeyboardNotification(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardHide), name: .UIKeyboardWillHide, object: nil)
}
var distanceOfKeyboardToTextView: CGFloat = 0
var activeTextElement: UIView?
var viewIsShifted = false
func keyboardShow(notification: NSNotification){
findActiveTextField(subviews: self.view.subviews, textField : &activeTextElement)
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue, let activeTextElement = activeTextElement, !viewIsShifted {
let viewYPosition = (activeTextElement.superview?.convert(activeTextElement.frame.origin, to: nil).y)! + activeTextElement.frame.height
let keyboardYPosition = view.frame.height - keyboardSize.height
distanceOfKeyboardToTextView = viewYPosition - keyboardYPosition
if distanceOfKeyboardToTextView > 0 {
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.customView.frame = CGRect(x: 0, y: self.customView.frame.origin.y - self.distanceOfKeyboardToTextView, width: self.customView.frame.width, height: self.customView.frame.height)
}, completion: nil)
viewIsShifted = true
}
}
activeTextElement = nil
}
func keyboardHide(){
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.customView.frame = CGRect(x: 0, y: self.customView.frame.origin.y + self.distanceOfKeyboardToTextView, width: self.customView.frame.width, height: self.customView.frame.height)
}, completion: nil)
viewIsShifted = false
distanceOfKeyboardToTextView = 0
}
func findActiveTextField (subviews : [UIView], textField : inout UIView?) {
guard textField == nil else { return }
for view in subviews {
if view.isFirstResponder {
textField = view
break
}
else if !view.subviews.isEmpty {
findActiveTextField (subviews: view.subviews, textField: &textField)
}
}
}
Update:
After tapping from an active textfield directly onto another textfield, keyboardShow is call, but since the view is already shifed, UIView.animate will not be performed. However, the view is resetted like no keyboard would be displayed, but since the other textfield is active, the keyboard is visible.
I dismiss the keyboard using this extension:
extension UIViewController {
//functions to hide the keyboard
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
and in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
observeKeyboardNotification()
self.hideKeyboardWhenTappedAround()
...
}