Increment Variable on horizontal UIScrollView slide - swift

How would I increment a variable when the user swipes to the right or left in my horizontal UIScrollView.
For example;
var pageNumber = 1
When the user swipes right it increments by 1 and when swipes left it decrements by 1 -
Here is my scrollView code that is initiated in the viewDidLoad function.
for (var i : Int = 0; i < numberOfQuestions ;i++)
{
//Construct the view by using the Template XIB file
var array : NSArray = NSBundle.mainBundle().loadNibNamed("QuestionView", owner: self, options: nil);
var view : QuestionView = array.objectAtIndex(0) as! QuestionView
// Set the scroll view to global variable
scrollViewQuiz = view
questionLabel.text = questions[currentQuestionIndexView].question
questionViews.addObject(view);
view.setTranslatesAutoresizingMaskIntoConstraints(false);
view.backgroundColor = UIColor.clearColor();
//Add to the scrollView
scrollView.addSubview(view);
//Add the top Constraints, they need to fit the superview
let views : NSDictionary = ["view" : view,"scrollView" : scrollView];
let constraints : NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view(==scrollView)]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: views as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
// Increments the question
currentQuestionIndexView++
}
contentView.backgroundColor = UIColor.clearColor();
var viewsDictionary : NSMutableDictionary = NSMutableDictionary(dictionary: ["scrollView" : scrollView]);
var visualFormat : NSMutableString = ("H:|").mutableCopy() as! NSMutableString;
//With all the views created, create the Layout Constraints for the horizontal logic
for (var i : Int = 0; i < numberOfQuestions; i++)
{
viewsDictionary.setValue(self.questionViews[i], forKey: NSString(format: "view%d", i) as String);
visualFormat.appendFormat("[view%d(==scrollView)]", i);
}
visualFormat.appendString("|");
constraints = NSLayoutConstraint.constraintsWithVisualFormat(visualFormat as String, options: NSLayoutFormatOptions.allZeros, metrics: nil, views: viewsDictionary as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
}
View Did Load UPDATE
override func viewDidLoad() {
// QUIZ LOGIC START
shuffleQuestions()
// UI CONSTRAINTS AND VIEW GENERATION
//Example of using 3 questions
var scrollView = self.scrollView;
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false);
self.view.setTranslatesAutoresizingMaskIntoConstraints(false);
self.contentView.setTranslatesAutoresizingMaskIntoConstraints(false);
//Constraints
var constraints : NSArray;
for (var i : Int = 0; i < numberOfQuestions ;i++)
{
//Construct the view by using the Template XIB file
var array : NSArray = NSBundle.mainBundle().loadNibNamed("QuestionView", owner: self, options: nil);
var view : QuestionView = array.objectAtIndex(0) as! QuestionView
// Set the scroll view to global variable
scrollViewQuiz = view
questionLabel.text = questions[currentQuestionIndexView].question
questionViews.addObject(view);
view.setTranslatesAutoresizingMaskIntoConstraints(false);
view.backgroundColor = UIColor.clearColor();
//Add to the scrollView
scrollView.addSubview(view);
//Add the top Constraints, they need to fit the superview
let views : NSDictionary = ["view" : view,"scrollView" : scrollView];
let constraints : NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view(==scrollView)]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: views as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
let leftSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleLeftSwipe:")
leftSwipeRecognizer.direction = .Left
scrollViewQuiz!.addGestureRecognizer(leftSwipeRecognizer)
let rightSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleRightSwipe:")
rightSwipeRecognizer.direction = .Right
scrollViewQuiz!.addGestureRecognizer(rightSwipeRecognizer)
// Increments the question
currentQuestionIndexView++
}
contentView.backgroundColor = UIColor.clearColor();
var viewsDictionary : NSMutableDictionary = NSMutableDictionary(dictionary: ["scrollView" : scrollView]);
var visualFormat : NSMutableString = ("H:|").mutableCopy() as! NSMutableString;
//With all the views created, create the Layout Constraints for the horizontal logic
for (var i : Int = 0; i < numberOfQuestions; i++)
{
viewsDictionary.setValue(self.questionViews[i], forKey: NSString(format: "view%d", i) as String);
visualFormat.appendFormat("[view%d(==scrollView)]", i);
}
visualFormat.appendString("|");
constraints = NSLayoutConstraint.constraintsWithVisualFormat(visualFormat as String, options: NSLayoutFormatOptions.allZeros, metrics: nil, views: viewsDictionary as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
}
Update 3 ##
//Add to the scrollView
scrollView.addSubview(view);
scrollView.addSubview(scrollViewQuiz!)
Update 4

I'm assuming you can't use the paging property of UIScrollView.
Contrary to the previous answer, you will actually need two different swipe recognizers. See https://stackoverflow.com/a/7760927/5007059
I understand that you want to detect swipes on the scroll view. To accomplish this, you could add two UISwipeGestureRecognizers to your scrollView, one for left swipes and one for right swipes like so:
// add to viewDidLoad
let leftSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleLeftSwipe:")
leftSwipeRecognizer.direction = .Left
scrollView.addGestureRecognizer(leftSwipeRecognizer)
let rightSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleRightSwipe:")
rightSwipeRecognizer.direction = .Right
scrollView.addGestureRecognizer(rightSwipeRecognizer)
...
// add to your view controller subclass
func handleLeftSwipe(sender: UISwipeGestureRecognizer) {
self.pageNumber = min(PageCount, self.pageNumber + 1)
}
func handleRightSwipe(sender: UISwipeGestureRecognizer) {
self.pageNumber = max(0, self.pageNumber - 1)
}

create swipe gesture
add gesture to the view
var pageNumber = 1
override func viewDidLoad() {
super.viewDidLoad()
///////
let swipeGesture = UISwipeGestureRecognizer(target: self, action: "swipeHandler:")
swipeGesture.direction = .Left | .Right
scrollViewQuiz.addGestureRecognizer(swipeGesture)
}
func swipeHandler( recognizer:UISwipeGestureRecognizer){
switch(recognizer.direction){
case UISwipeGestureRecognizerDirection.Left:
pageNumber--
case UISwipeGestureRecognizerDirection.Right:
pageNumber++
default: break
}
}

Related

Swift Scrollview scroll so that button is right above keyboard when keyboard appears

I have tried literally every solution on the internet to get this answer, and none of them work without bugs. I have a stackview embedded in a scrollview, and I want the signInButton to be right above the keyboard.
Here is my current attempted solution:
#objc func keyboardWillShow(notification: NSNotification) {
guard let userInfo: NSDictionary = notification.userInfo as NSDictionary?,
let keyboardInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let keyboardSize = keyboardInfo.cgRectValue.size
scrollView.isScrollEnabled = false
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var rect = self.view.frame
rect.size.height -= keyboardSize.height
var frame = self.signInButton.frame
frame.size.height += 50
self.scrollView.scrollRectToVisible(frame, animated: true)
}
#objc func keyboardWillHide(notification: NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInset
scrollView.isScrollEnabled = false
}
But this does not come close to working on all screen sizes. While the button always appears sometimes it appears way above the keyboard, and other times slightly above. And when the keyboard disappears on some screens it doesn't work either. I'm curious if anyone knowledgeable can give a robust answer that many can use for this ubiquitous problem. Swift 5 please.
I know I can use a tableview, but I find it incredibly sad that iOS does not have a clear, alternative working solution without embedding everything in a tableview.
Thanks!
I have had similar problems and i fixed it by using this. (probably not the best way, but it works)
Add these in viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboarddismissedlogin(notification:)), name: Notification.Name("keyboarddismissedlogin"), object: nil)
Define those 3 functions
// MARK: - KeyboardON
#objc func keyboardWillShow(notification:NSNotification) {
let userInfo:NSDictionary = notification.userInfo! as NSDictionary
let keyboardFrame:NSValue = userInfo.value(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
TableBottomConstraint.constant = TableBottomConstraint.constant + keyboardHeight
extraFunctions.keyboardHeight = Int(keyboardHeight)
}
// MARK: - keyboardOFFheight
#objc func keyboardWillHide(notification:NSNotification) {
let userInfo:NSDictionary = notification.userInfo! as NSDictionary
let keyboardFrame:NSValue = userInfo.value(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
if TableBottomConstraint.constant > keyboardHeight
{
TableBottomConstraint.constant = TableBottomConstraint.constant - keyboardHeight
}
}
// MARK: - Keyboard Dismiss
#objc func keyboarddismissedlogin(notification: Notification) {
TableBottomConstraint.constant = TableBottomConstraint.constant - CGFloat(extraFunctions.keyboardHeight)
}
Here you have to make an outlet of your table or scroll view's bottom constraint.
extraFunctions.keyboardHeight
is simply a struct that i created to store the keyboard height. (you can simply create a variable instead of struct - > variable. I did it just for the sake of naming it)
This will simply move your view UP from the bottom when keyboard appears and changes back to original position when keyboard disappears.
All this works , assuming that your BUTTON is at the bottom of your view.
I donot fully get your question. In my case, I put the constraint to the bottom of the button, and set its constant when the keyboard appear/ dissappaear.
#IBOutlet weak var edittingBottomConstraint: NSLayoutConstraint!
#objc func showKeyboard(notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyBoardHeight = keyboardRectangle.size.height
edittingBottomConstraint.constant = -keyBoardHeight
}
}

Swift - Dismiss Keyboard and call TouchUpInside Button Simultaneously not working when using UIKeyboardWillChangeFrame

I am using swift and having issues with TouchUpInside: if I'm using UIKeyboardWillChangeFrame or UIKeyboardWillShow/UIKeyboardWillHide, & the keyboard is showing, & the button I'm trying to press is behind the keyboard when keyboard is shown initially. (If I scroll down to the button till visible and press, no touchUpInside called).
TouchDown seems to work consistently whether the keyboard is showing or not, but TouchUpInside is not called. If the button is above the top of the keyboard when the keyboard is initially shown, TouchUpInside works. I'm using keyboardNotification to set the height of a view below my scrollView in order to raise up my scrollView when keyboard is showing. From what I can see it's only usually when the button is the last element in the scrollView (and therefore likely to be behind the keyboard when keyboard shown).
#IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var saveButton: UIButton!
#IBAction func saveTouchUpInside(_ sender: UIButton) {
print("touchupinside = does not work")
}
#objc func saveTouchDown(notification:NSNotification){
print("touchdown = works")
}
viewWillAppear:
textField.delegate = self
NotificationCenter.default.addObserver(self,selector:#selector(self.keyboardNotification(notification:)),name:
NSNotification.Name.UIKeyboardWillChangeFrame,object: nil)
self.saveButton.addTarget(self, action:#selector(ViewController.saveTouchDown(notification:)), for: .touchDown)
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrameY >= UIScreen.main.bounds.size.height {
self.keyboardHeightLayoutConstraint?.constant = 0.0
} else {
self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
}
UIView.animate(withDuration: duration, delay: TimeInterval(0),options: animationCurve, animations: { self.view.layoutIfNeeded() }, completion: nil)
}
}
I would like to dismiss the keyboard and call saveTouchUpInside at the same time, without using TouchDown.
I abstract the keyboard interaction as a separate class so that my controllers do not get bloated(also follows separation of concerns). Here is the keyboard manager class that I use.
import UIKit
/**
* To adjust the scroll view associated with the displayed view to accommodate
* the display of keyboard so that the view gets adjusted accordingly without getting hidden
*/
class KeyboardManager {
private var scrollView: UIScrollView
/**
* -parameter scrollView: ScrollView that need to be adjusted so that it does not get clipped by the presence of the keyboard
*/
init(scrollView: UIScrollView) {
self.scrollView = scrollView
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self,
selector: #selector(adjustForKeyboard),
name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.addObserver(self,
selector: #selector(adjustForKeyboard),
name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
}
/**
* Indicates that the on-screen keyboard is about to be presented.
* -parameter notification: Contains animation and frame details on the keyboard
*
*/
#objc func adjustForKeyboard(notification: Notification) {
guard let containedView = scrollView.superview else { return }
let userInfo = notification.userInfo!
let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let keyboardViewEndFrame = containedView.convert(keyboardScreenEndFrame, to: containedView.window)
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
let rawAnimationCurveValue = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber).uintValue
UIView.animate(withDuration: TimeInterval(truncating: duration),
delay: 0,
options: [UIView.AnimationOptions(rawValue: rawAnimationCurveValue)],
animations: {
if notification.name == UIResponder.keyboardWillHideNotification {
self.scrollView.contentInset = UIEdgeInsets.zero
} else {
self.scrollView.contentInset = UIEdgeInsets(top: 0,
left: 0,
bottom: keyboardViewEndFrame.height,
right: 0)
}
self.scrollView.scrollIndicatorInsets = self.scrollView.contentInset
},
completion: nil)
}
deinit {
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver(self)
}
}
Its usage is like this
create a reference to the keyboard manager
private var keyboardManager: KeyboardManager!
and assign the keyboard manager class like below in viewDidLoad where self.scrollView is the scrollView that you are working with
self.keyboardManager = KeyboardManager(scrollView: self.scrollView)
This should take care of the issue. If that does not work, probably a sample project might help to take a deep dive into that.

Passing parameters in a clickable UIabel

I'm trying to make a clickable UILabel by following with this code:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(ViewController.ale_plato1))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
...
#objc
func ale_plato1(sender: UITapGestureRecognizer){
let label = sender.view
print ("tapped!")
}
This works well. But I want to pass parameters to the function. Something like this:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(MenuController.ale_plato1("parameter")))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
#objc
func ale_plato1(sender: UITapGestureRecognizer, parameterRecived: String){
}
But I don't know how do that in swift 3...
Any help? Thanks you
Wherever you make your multiple UILabel set each label to a different tag.
for i in 0..<3 {
let label = UILabel()
label.tag = i
let tap = UITapGestureRecognizer(target: self, action: #selector(tap))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap_plato)
}
#objc func tap(sender: UITapGestureRecognizer) {
let label = sender.view as! UILabel // if you are SURE that your tap will be a UILabel
if(label.tag == 0) {
label.text = "This is label 0"
if(label.tag == 1) {
label.text = "This is label 1"
}
}
Threw this up in a jiffy, hop over the syntax issues if there are any.
Here we can create labels with tags and apply the same tap to them. From there, we can check what tag it is inside the tap method and do something from there.
You could use the tag attribute for Int values, but if you want anything other than that:
Create a custom UILabel Class with a value variable.
class LabelWithValue : UILabel {
var value : String = ""
}
Then you can use it as a normal Label
let label = LabelWithValue()
label.text = "bla"
label.value = "Anything"
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleGetText)))
Then you can get it's text and it's value.
#objc func handleGetText(_ sender : UITapGestureRecognizer) {
if let label = sender.view as? LabelWithValue {
print(label.text)
print(label.value)
}
}

Looping UITapGestureRecognizer only added to last one not all of them

So I'm up to the point that I'm creating UIViews programmatically based on the amount of an array which is returned from JSON. Everything seems to be going ok except for the loop which is supposed to add the tap to each of the views but it is only adding it to one view. Only prints on the last UIView.
func addsubviews(howmany: Int) -> Void{
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
for x in 1...howmany{
guard let v = UINib(nibName: "DealsView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as? UIView else { return }
v.translatesAutoresizingMaskIntoConstraints = false
guard let lbl = v.viewWithTag(99) as? UILabel else { return }
lbl.text = "Here is a new Label for Loop: " + "\(x)"
self.allContainer.addSubview(v)
v.backgroundColor = UIColor.white
var horizontalConstratint1 = NSLayoutConstraint()
var horizontalConstratint2 = NSLayoutConstraint()
var verticalConstraint1 = NSLayoutConstraint()
heightConstraint = v.heightAnchor.constraint(equalToConstant: 100)
horizontalConstratint1 = v.leadingAnchor.constraint(equalTo: (v.superview?.leadingAnchor)!, constant: 10)
horizontalConstratint2 = v.trailingAnchor.constraint(equalTo: (v.superview?.trailingAnchor)!, constant: -10)
if prevView == nil{
verticalConstraint1 = v.topAnchor.constraint(equalTo: (v.superview?.topAnchor)!, constant: 20)
}else
{
if let pv = prevView as? UIView {
verticalConstraint1 = v.topAnchor.constraint(equalTo: (pv.bottomAnchor), constant: 20)
}
}
NSLayoutConstraint.activate([horizontalConstratint1,horizontalConstratint2,verticalConstraint1])
prevView = v
}
if let pv = prevView as? UIView {
print("final constratint")
NSLayoutConstraint.activate([
self.allContainer.bottomAnchor.constraint(equalTo: pv.bottomAnchor, constant: 20)
])
}
for views in self.allContainer.subviews{
print("adding views")
views.addGestureRecognizer(tap)
}
}
After some trial and error...
The tap recognizer added for each loop:
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
the action which will be where the action goes on the tap:
func handleTap(sender: UITapGestureRecognizer) {
// You can get the view here by writing
let myv = sender.view
print(myv)
}
You have just a single UITapGestureRecognizer before your view loop starts. AFAIK, tap gesture recognizers can only be attached to one view at a time. Check out this question. To solve this, try writing this line:
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
inside your view loop.
To find out what view was tapped, make your selector handleTap take an argument like so:
Selector("handleTap:")
and the method itself like this:
func handleTap(sender: UITapGestureRecognizer? = nil) {
// You can get the view here by writing
let v = sender.view
}

swift uitapgesturerecognizer pass parameters

I have a lot of Buttons created programmatically and that can change anytime. my tap gesture :
let apriVideoGesture = UITapGestureRecognizer(target: self, action: #selector(PrincipaleController.apriVideo(_:)))
cont.addGestureRecognizer(apriVideoGesture)
func apriVideo(sender : UITapGestureRecognizer){
}
how can i pass parameters? somethig like this :
let apriVideoGesture = UITapGestureRecognizer(target: self, action: #selector(PrincipaleController.apriVideo(stringa : "ciao")))
cont.addGestureRecognizer(apriVideoGesture)
func apriVideo(sender : UITapGestureRecognizer, stringa : String){
}
sorry for bad english, i'm italian
First of all, if you are using button then why are you adding tap gesture? You can add target to it as
btn.addTarget(self, action: #selector(self.btnPressed(_:)), forControlEvents: .TouchDragInside)
But still you can achieve you goal using tap gesture as
Using UIView as u have insisted
class ViewController: UIViewController {
let arrayOfSongsURL: [String] = []
let startingTag = 100
override func viewDidLoad() {
super.viewDidLoad()
let height : CGFloat = 100
let width : CGFloat = 100
(arrayOfSongsURL as NSArray).enumerateObjectsUsingBlock { (url, index, finished) -> Void in
let v = UIView(frame: CGRectMake(0, CGFloat(index) * height, width, height))
v.tag = self.startingTag + index
v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTapGesture(_:))))
self.view.addSubview(v)
}
// Do any additional setup after loading the view, typically from a nib.
}
#objc func handleTapGesture(gesture : UITapGestureRecognizer)
{
let v = gesture.view!
let tag = v.tag
let songURL = arrayOfSongsURL[tag - startingTag]
//Do what you want to do with songURL
}
}