I want to create an extension for UIViewController but I have one problem with my @objc func. How to pass this parameter below?
#selector(handlePanGestExt(sender: cardOriginY: )))
fileprivate var cardOriginY : CGFloat!
extension UIViewController {
func panGestureRecognizerToHandleDragAndDissmisView(inCardView : UIView, cardOriginY : CGFloat) {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestExt(sender: cardOriginY: )))
inCardView.addGestureRecognizer(panGesture)
}
@objc func handlePanGestExt(sender: UIPanGestureRecognizer, cardOriginY : CGFloat) {
let fileView = sender.view!
switch sender.state {
case .began, .changed:
moveViewWithPan(view: fileView, sender: sender)
case .ended:
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1300 {
dismiss(animated: false, completion: nil)
} else {
returnViewToOrigin(view: fileView, cardOriginY: cardOriginY)
}
default:
break
}
}
func moveViewWithPan(view: UIView, sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
guard translation.y >= 0 else { return }
view.center = CGPoint(x:view.center.x, y: view.center.y translation.y)
sender.setTranslation(CGPoint.zero, in: view)
}
func returnViewToOrigin(view: UIView, cardOriginY : CGFloat) {
UIView.animate(withDuration: 0.3) {
view.frame.origin = CGPoint(x: 0.0 , y: cardOriginY)
}
}
what am I doing wrong ? When I am calling func "panGestureRecognizerToHandleDragAndDissmisView" I want to pass this parameter "cardOriginY"
CodePudding user response:
UIPanGestureRecognizer
's selector can take only one argument and it's UIPanGestureRecognizer
itself. Basically it should look like this
@objc func handlePanGestExt(_ sender: UIPanGestureRecognizer) {
// your code here
}
In your concrete situation, you need to pass one more argument for your calculations cardOriginY
. I can propose you to do the next thing:
- Add
DismissableCardContainer
for reusing ability - Add
PanGestureActionHandler
to avoid unnecessary complications with @objc modifiers - Implement
DismissableCardContainer
protocol for your purpose
Here's the code:
protocol DismissableCardContainer: AnyObject {
func addPanGestureRecognizerToHandleDragAndDissmis(
to cardView : UIView,
cardOriginY : CGFloat
)
}
extension DismissableCardContainer where Self: UIViewController {
func addPanGestureRecognizerToHandleDragAndDissmis(
to cardView : UIView,
cardOriginY : CGFloat
) {
let panGesture = PanGestureActionHandler(
action: { [weak self] gesture in
self?.handlePanGestExt(
gesture,
cardOriginY
)
}
)
cardView.addGestureRecognizer(panGesture)
}
private func handlePanGestExt(
_ sender: UIPanGestureRecognizer,
_ cardOriginY: CGFloat
) {
let fileView = sender.view!
switch sender.state {
case .began, .changed:
moveViewWithPan(view: fileView, sender: sender)
case .ended:
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1300 {
dismiss(animated: false, completion: nil)
} else {
returnViewToOrigin(view: fileView, cardOriginY: cardOriginY)
}
default:
break
}
}
private func moveViewWithPan(view: UIView, sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
guard translation.y >= 0 else { return }
view.center = CGPoint(x:view.center.x, y: view.center.y translation.y)
sender.setTranslation(CGPoint.zero, in: view)
}
private func returnViewToOrigin(view: UIView, cardOriginY : CGFloat) {
UIView.animate(withDuration: 0.3) {
view.frame.origin = CGPoint(x: 0.0 , y: cardOriginY)
}
}
}
final class PanGestureActionHandler: UIPanGestureRecognizer {
typealias Callback = ((PanGestureActionHandler) -> Void)
private let action: PanGestureActionHandler.Callback
init(
action: @escaping PanGestureActionHandler.Callback
) {
self.action = action
super.init(target: nil, action: nil)
addTarget(
self,
action: #selector(self.handleGestureAction)
)
}
@objc private func handleGestureAction(_ gr: UIPanGestureRecognizer) {
action(gr as! PanGestureActionHandler)
}
}
After that there's only left to conform DismissableCardContainer
protocol where you need it