Xcode Swipe Gesture doesn't change constraint constant - ios

I have an issue with the swipe gesture recognizer in Xcode. What I want to let the code do is the following: When you swipe upwards a view with floating buttons disappears. When you swipe downwards, the floating buttons appear again. But of course, it doesn't work :D
I can change the position of the view manually in StoryBoard without messing the constraints up.
Everything is connected properly.
It doesn't give an error message either, just doesn't work.
What can I do?
I use the following code:
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var BottomFloatersBottomLayoutGuide: NSLayoutConstraint!
#IBAction func UpSwipe(_ sender: UISwipeGestureRecognizer) {
BottomFloatersBottomLayoutGuide.constant = -400
}
#IBAction func DownSwipe(_ sender: UISwipeGestureRecognizer) {
BottomFloatersBottomLayoutGuide.constant = 0
}
}

You have to put it in this function:
UIView.animate(withDuration: 1.0){
//your code here
}
Everything in that block will animate: change colors, a view's frame etc. So also your constrains.
Edit: Woops, I am wrong. In UIView.animate, self.view.layoutIfNeeded() needs to be called. The change of your constrains should be outside that block. layoutIfNeeded() updates the constrains.
Your code should look like this:
#IBAction func UpSwipe(_ sender: UISwipeGestureRecognizer) {
BottomFloatersBottomLayoutGuide.constant = -400
UIView.animate(withDuration: 1.0){
self.view.layoutIfNeeded()
}
}

Related

Why are animated calls of UIView.isHidden = false compounded?

As you can see I'm having trouble formulating the question. Let me try to explain:
I'm using a search bar in my swift ios app. In order to get a desired animation effect I put it in a vertical stack view and then animate its isHidden property. This way search bar pushes down other children of the stack view as it animates in or pulls them up as it animates out. So far so good.
I've noticed a behavior that I think is strange. Could be a bug or could be me not understanding how things work. Basically if I call search bar hiding method x times in a row I need to call search bar showing method x times before it would show. I'd expect to have to make just one call to show search bar regardless of how many times I called hiding method. The issue doesn't exist other way around: if I call search bar showing code x times I only need to call hiding method once for it to go away. This doesn't happen if I set isHidden without animating it...
Here's a sample code and a video of the issue. I'd appreciate it if someone would help me understand this behavior.
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.isHidden = true
}
}
#IBAction func showAction(_ sender: UIButton) {
expandSearch()
}
#IBAction func hideAction(_ sender: UIButton) {
collapseSearch()
}
private func expandSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = false
}
}
private func collapseSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = true
}
searchBar.resignFirstResponder()
}
}
You should not call an asynchronous animation of searchbar x times, instead of I suggest you to keep its state in variable, something like isSearchBarHidden,
and check it before show/hide search bar. You could use just one method with such signature showSearchBar(show: Bool) and setting this variable there.
#IBAction func showAction(_ sender: UIButton) {
showSearchBar(true)
}
#IBAction func hideAction(_ sender: UIButton) {
showSearchBar(false)
}
private
func showSearchBar(_ show: Bool) {
guard isSearchBarHidden != show else {
return
}
UIView.animate(withDuration: 0.3, animations: {
self.searchBar.isHidden = show
}) {
self.isSearchBarHidden = show
if !show && searchBar.isFerstResponder {
searchBar.resignFirstResponder
}
}
}
private
var isSearchBarHidden: Bool = true
Also it is a good practice to check if your textView/textField/searchBar isFirstResponder before call resignFirstResponder.
Hope it will help. Good luck!

IOS Segmented Control: Can't Change Selected Segment

I created a segmented control in my storyboard that looks like this:
Then I created an IBAction for when the control's value changes:
#IBAction func segmentValChanged(_ sender: UISegmentedControl) {
print("touched")
if (sender.selectedSegmentIndex == 0) {
sender.selectedSegmentIndex = 1;
}
else{
sender.selectedSegmentIndex = 0;
}
}
The only issue is that this function is never called!
When I click the Jokes (Beta) button, nothing happens.
However, when I created an IBOutlet for the control and tried to change the selected segment in my viewDidLoad(), it worked:
segmentedController.selectedSegmentIndex = 1
I know I probably missed something obvious, since I've never used a segmented control before.
Thanks so much if anyone can point out what this is.
Cheers!
import UIKit
import Alamofire
class ViewController: UIViewController {
var currentImage: CurrentImage!
var parameters: Parameters!
#IBOutlet weak var segmentedController: UISegmentedControl!
#IBAction func segmentValChanged(_ sender: UISegmentedControl) {
print("touched")
if (sender.selectedSegmentIndex == 0) { // switch to 1
sender.selectedSegmentIndex = 1;
}
else{
sender.selectedSegmentIndex = 0;
}
}
override func viewDidLoad() {
super.viewDidLoad()
segmentedController.selectedSegmentIndex = 1
self.segmentedController.addTarget(self, action: #selector(segmentValChanged(_:)), for: .valueChanged)
}
}
And this is what it looks like when I right click the view:
Whenever I see this it is almost always the same problem... If the segmented control is being drawn outside of its parent view, then it will not respond to taps. This is most likely the problem you are having. (This can also happen with UIButtons.)
Ways to test if this is the problem:
set clipsToBounds of the segmented control's parent to true. If the control no longer shows on the screen, then it is being drawn outside of it's parent view. segmentedControl.superview?.clipsToBounds = true
add a border to the segmented control's parent. If the control is being drawn outside the border then there you go. segmentedControl.superview?.layer.borderWidth = 1
Solve the problem by expanding the size of the parent view so the control is being drawn within it.
On a side note, your IBAction is incorrect. When the value changed action is called, the segmented control will already have changed its value and the new segment will be highlighted. You don't have to do it manually. This is another indicator that your control isn't being drawn in the bounds of its parent view.

Changing the appearance of my #IBAction button in Swift out of its func

I'm writing an app in which there's a scrollview and what I want is to set the button's alpha to 0 at first and change it into 1 when the user scrolls to the last page. I'm writing code like this:
#IBAction func startButton(sender: UIButton) {
sender.layer.cornerRadius = 2.0
sender.alpha = 0.0
}
extension UserGuideViewController: UIScrollViewDelegate {
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset
pageControl.currentPage = Int(offset.x / view.bounds.width)
if pageControl.currentPage == numberOfPages - 1 {
UIView.animateWithDuration(0.5) {
self.startButton.alpha = 1.0
}
} else {
UIView.animateWithDuration(0.2) {
self.startButton.alpha = 0.0
}
}
}
}
and it says the Value of type (UIButton)->() has no member alpha. I don't know how to do this.
I know it would be easy if this button is an #IBOutlet but I need to set it as an #IBAction so that when it gets touched I can show another view controller.
You need to create both an #IBOutlet and an #IBAction for the button. Drag twice from Interface Builder to your view controller and select the appropriate values (outlet or action) from the popup menu and give them different names. Then access the outlet in your scrollViewDidScroll method.

Dismissing the keyboard in a UIViewController

I'm running this code within a ViewController class that contains UIViewController and SettingsViewDelegate. The View Controller scene contains a text view that has a scroll bar.
#IBOutlet weak var tvEditor: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Hide keyboard via swipeDownGestureRecognizer.
let swipeGesture: UISwipeGestureRecognizer = UISwipeGestureRecognizer (target: self, action: "hideKeyboard")
swipeGesture.cancelsTouchesInView = false
UISwipeGestureRecognizerDirection.Down
}
func hideKeyboard() {
tvEditor.resignFirstResponder()
}
When I run my project in the iOS Simulator/device, the keyboard does not respond to a downward swipe. I change the orientation of my device to landscape, and then back to portrait, which is when the keyboard disappears. Can someone please tell me where I'm going wrong?
First, Please add swipeGesture to your viewcontroller.
And...
func hideKeyboard() {
self.view.endEditing(true)
}
It'll works fine for your project.
Hope it'll help you.

Why isn't my UISlider animating?

According to Apple's documentation, it is possible to programmatically set the value of a UISlider with a smooth animation. I'm attempting to do so from a custom view controller, the UI is being defined from a storyboard.
The Context
In my example I'm attempting to update the slider value from a custom view controller, the UI is being defined from a storyboard. The example only renders a single slider.
When the user releases the slider, the value is reset to 0.
The Code
import UIKit
class ViewController: UIViewController {
#IBOutlet var mySlider: UISlider!
#IBAction func resetSlider() {
mySlider.setValue(0, animated:true)
NSLog("Reset!")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
resetSlider is linked to the Touch Up Inside event.
The Problem
When resetSlider is called the value does change on the interface, but it does not animate (the value simply "jumps" to 0). My goal is to have the value gracefully shift back to zero.
Note: "Reset!" only displays once (per click), which indicates that resetSlider is not being called multiple times.
Why isn't this UISlider animating?
The Video
Since IB is so visual, here is a video of the situation, password is code
the setValue animated parameter doesn't actually perform an animation, but rather it enables animation.
To trigger the animation, you need to use UIView.animateWithDuration, and pass in the setValue command as the animation:
UIView.animateWithDuration(0.2, animations: {
self.mySlider.setValue(0, animated:true)
})
Swift 5
UIView.animate(withDuration: 0.2, animations: {
self.mySlider.setValue(0, animated:true)
})
The solution of #Slifty is correct. But if you want UISlider is as smooth as defauft, so you can decrease the time animation to below 0.2. It may work such as 0.1/2 or 0.1/3.

Resources