Swift3 unable to implement the function in another class - ios

I have added gesture in classA for upswipe, If swipe up then the function available in classB has to be called, while calling classB function in classA the function is getting called but the UI changes are not made in classB.
Thanks in advance
class A: UIViewController {
var swipeUp = UISwipeGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGestureUp))
swipeUp.direction = UISwipeGestureRecognizerDirection.up
self.view.addGestureRecognizer(swipeUp)
}
func respondToSwipeGestureUp(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.up:
UserDefaults.standard.set("open", forKey: "status")
UserDefaults.standard.synchronize()
print("Swiped up")
B().moveUp()
default:
break
}
}
}
}
class B: UIViewController {
func moveUp() {
let status = UserDefaults.standard.object(forKey: "status") as! String
if status .isEqual("open")
{
let fullView1: CGFloat = 100
self.view.frame = CGRect(x: 0, y: fullView1, width: self.view.frame.width, height: self.view.frame.height)
}
else
{
}
}
}

Class B UI does not change because you didn't present B controller on navigation. I mean to say If you controller A is currently present on screen and on SwipeGesture event you are updating the UI using controller B whose is not present on screen yet. Why are you doing this and when Did you Push controller B.
I hope you got your answer.

Make your method as class method
class func moveUp() {
// your code
}
And call it as:
B.moveUp()

Related

why the "shouldReceiveTouch" returns "NO", the "PanGesture" still works?

Such as title, I have a superView A and a childView B. The A has a panGestureRecognizer. When I swipe the B, it will trigger the panGestureRecognizer of A. So I return No in the shouldReceiveTouch of A, But the panGestureRecognizer still works whick makes me confused.
I used the following and it seems to work as expected:
class ViewController: UIViewController {
private lazy var topView: UIView = {
let view = UIView(frame: .init(x: 100.0, y: 200.0, width: 200.0, height: 200.0))
view.backgroundColor = UIColor.green
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
let bottomView = self.view
bottomView?.backgroundColor = UIColor.red
bottomView?.addSubview(topView)
bottomView?.addGestureRecognizer({
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPan))
panGesture.delegate = self
return panGesture
}())
}
private var stateString: String = "" {
didSet {
if stateString != oldValue {
print("State changed to \(stateString)")
}
}
}
#objc private func onPan(_ sender: UIGestureRecognizer) {
switch sender.state {
case .began: stateString = "begin"
case .changed: stateString = "changed"
case .ended: stateString = "ended"
case .cancelled: stateString = "canceled"
default: stateString = "some thing else"
}
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return topView.bounds.contains(touch.location(in: topView)) == false
}
}
A gesture only works when started out of the green view.
Once a gesture has started then events will be triggered normally as they should, that includes within the green view.

Issue performing a segue when function called from another class

I have an Intro video section for an app. The storyboard has a segue "toMainMenu" that links to another storyboard view controller.
The IntroVideoVC has two classes inside it:-
class IntroVideoVC: UIViewController {
var videoView: IntroVideoView!
override func viewDidLoad() {
videoView = IntroVideoView(controller: self)
self.view.addSubview(videoView)
let tap = UITapGestureRecognizer(target: self, action: #selector(tappedVideo))
view.addGestureRecognizer(tap)
}
#objc func tappedVideo() {
videoFinished();
}
func videoFinished() {
self.performSegue(withIdentifier: "toMainMenu", sender: self)
}
}
class IntroVideoView: UIView {
init(controller: UIViewController) {
super.init(frame: CGRect(x: 0, y: 0, w: 0, h: 0))
self.controller = controller
...
}
func handleVideo(videoPlayer: AVPlayer) {
...
IntroVideoVC().videoFinished()
}
}
If i tap the video it correctly performs the segue, however if i let the video end which in-turn calls IntroVideoViews -> handleVideo() method i get a crash stating it has no segue with identifier "toMainMenu".
I think i understand why but unsure how to get around the issue being new to swift. I believe it is because handleVideo() is using IntroVideoVC as a singleton and so is losing reference to IntroVideoVC's controller! I maybe (read: likely) be wrong but that is what i feel may be causing this issue.
Just looking for a nudge in the right direction
thanks in advance
You could use protocol to solve that, it would be something like this:
protocol IntroVideoDelegate {
func videoFinished()
}
class IntroVideoVC: UIViewController, IntroVideoDelegate {
var videoView: IntroVideoView!
override func viewDidLoad() {
videoView = IntroVideoView(controller: self)
videoView.videoDelegate = self
self.view.addSubview(videoView)
let tap = UITapGestureRecognizer(target: self, action: #selector(tappedVideo))
view.addGestureRecognizer(tap)
}
#objc func tappedVideo() {
videoFinished();
}
func videoFinished() {
self.performSegue(withIdentifier: "toMainMenu", sender: self)
}
}
class IntroVideoView: UIView {
videoDelegate: IntroVideoDelegate?
init(controller: UIViewController) {
super.init(frame: CGRect(x: 0, y: 0, w: 0, h: 0))
self.controller = controller
...
}
func handleVideo(videoPlayer: AVPlayer) {
...
videoDelegate.videoFinished()
}
}
Remember that the videoView is still a subview to IntroVideoVC, so if the app goes back to this VC the user will see it, so if you don't want that to happen, be sure to remove it with:
videoView.removeFromSuperview()
In the videoFinished() func
If you want to do running a VC func from the view you should get the parent of IntroVideoView, here is how: Given a view, how do I get its viewController? But i'm pretty sure it breaks MVC

Pass UILongPressGestureRecognizer between UIViewControllers

I've looked around on SO and other places online and haven't been able to find a solution yet. I have a UILongPressGestureRecognizer on one view controller which performs an animation on a UIView then presents another view controller (if the user does not move their finger during the animation). I would like to dismiss the second view controller when the user lifts their finger, but I am having trouble.
My current approach is adding another UILongPressGestureRecognizer to the second view controller and dismissing the view controller when its state is .ended; however I don't know how to tie the two gesture recognizers together so that I can start the touch in one view controller and end in another.
I have attached the relevant code below
In the first View Controller
#objc private func pinAnimation(sender: UILongPressGestureRecognizer) {
if let headerView = projectView.projectCollectionView.supplementaryView(forElementKind: UICollectionElementKindSectionHeader, at: IndexPath(row: 0, section: 0)) as? ProjectHeaderView {
switch sender.state {
case .began:
let frame = headerView.pinButton.frame
UIView.setAnimationCurve(.linear)
UIView.animate(withDuration: 1, animations: {
headerView.pinProgressView.frame = frame
}, completion: { (_) in
if (headerView.pinProgressView.frame == headerView.pinButton.frame) {
let notification = UINotificationFeedbackGenerator()
notification.notificationOccurred(.success)
let homeVC = HomeViewController()
self.present(homeVC, animated: true, completion: {
homeVC.longPress(sender)
let height = headerView.pinButton.frame.height
let minX = headerView.pinButton.frame.minX
let minY = headerView.pinButton.frame.minY
headerView.pinProgressView.frame = CGRect(x: minX, y: minY, width: 0, height: height)
})
}
})
case .ended:
//cancel current animation
let height = headerView.pinProgressView.frame.height
let minX = headerView.pinProgressView.frame.minX
let minY = headerView.pinProgressView.frame.minY
UIView.setAnimationCurve(.linear)
UIView.animate(withDuration: 0.5) {
headerView.pinProgressView.frame = CGRect(x: minX, y: minY, width: 0, height: height)
}
default:
break
}
}
}
In the second View Controller
override func viewDidLoad() {
super.viewDidLoad()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
view.addGestureRecognizer(longPressRecognizer)
}
#objc func longPress(_ sender: UILongPressGestureRecognizer) {
switch sender.state {
case .ended:
self.dismiss(animated: true, completion: nil)
default:
break
}
}
Any help / guidance will be much appreciated!
I would recommend you using delegate pattern:
Create delegate for your first ViewController
protocol FirstViewControllerDelegate {
func longPressEnded()
}
then add delegate variable to your first ViewController
class FirstViewController {
var delegate: FirstViewControllerDelegate?
...
}
next call method on delegate when long press ended
#objc private func pinAnimation(sender: UILongPressGestureRecognizer) {
...
switch sender.state {
case .began:
...
case .ended:
...
delegate?.longPressEnded()
default:
break
}
}
after that when you're presenting second ViewController, set first ViewController's delegate as homeVC
let homeVC = HomeViewController()
self.delegate = homeVC
then implement this delegate to second ViewController and define what should happen when long press ended
class HomeViewController: UIViewController, FirstViewControllerDelegate {
...
func longPressEnded() {
dismiss(animated: true, completion: nil)
}
}

I am defining gesture recognizers in an ImageView subclass but calling a method in the ViewController crashes with "unrecognized selector"

I am defining gesture recognizers in an ImageView subclass but calling a method in the ViewController crashes with "unrecognized selector". The gesture works if I define the gestures in the VC, but I'm trying to put the code in the custom view class to cut down on clutter.
Is this possible? This is what I'm doing:
class DetailPhotoImageView: UIImageView {
func setupGestures(){
let tapped = UITapGestureRecognizer(target: self, action: #selector(TripDetailVC.tapped(_:)))
tapped.numberOfTapsRequired = 2
addGestureRecognizer(tapped)
}
}
And then in the VC I have this function and call imageView.setupGestures()
func tapped(_ gesture:UIGestureRecognizer) {
if let tapGesture = gesture as? UITapGestureRecognizer {
switch tapGesture.numberOfTapsRequired {
case 2:
print("Worked")
default:
break
}
}
}
So going off of Randall Wang's suggestion, they are right, you should could also go about this another, space-saving way. Pass the viewController as a parameter and set the it as the target:
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
func setupGestures(_ target: UIViewController) {
guard let aTarget = target as? MyViewController else { return }
let tap = UITapGestureRecognizer(target: aTarget, action: #selector(aTarget.someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
}
And in your viewController class:
detailPhoto.setupGestures(self)
Make a protocol for your UIImageView that takes in the gesture and set your ViewController as the delegate (having the ImageView as a parameter as well is just good practice.. or common):
protocol DetailPhotoDelegate {
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor gesture: UITapGestureRecognizer)
}
Add a delegate variable to you UIImageView subclass and preform your protocol function when the view is tapped. Also, make sure your UIImageView is a UIGestureRecognizerDelegate.
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
var delegate: DetailPhotoDelegate?
func setupGestures() {
let tap = UITapGestureRecognizer(target: self, action: #selector(someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
func someAction(_ guesture: UITapGestureRecognizer) {
print("tap - subclass func")
guard let aDelegate = aDelegate else { assertionFailure(); return }
delegate.detailPhoto(self, actionFor: gesture)
}
}
Then add the protocol to your ViewController, set it as your DetailPhotoImageView's delegate somewhere prior to the action being implemented (I did it in viewDidLoad() for the example), and implement the protocol method however you wish:
class ViewController: UIViewController, DetailPhotoDelegate {
#IBAction weak var detailPhoto: DetailPhotoImageView!
override func viewDidLoad() {
super.viewDidLoad()
detailPhoto.delegate = self
}
// Mark: DetailPhotoDelegate
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor guesture: UITapGestureRecognizer) {
switch guesture.numberOfTapsRequired {
case 1: print("1 tap"); return
case 2: print("2 taps"); return
default: print("\(guesture.numberOfTapsRequired) taps"); return
}
}
I think target shouldn't be self

Extension,Selector & add subview via Extension

I have a common functionality of displaying a corner settings list in almost all pages of my application. I have thought of using extension to achieve this common functionality.
My Code -
I created a NSObject subclass and within it had an extension of UIViewController -
import UIKit
class viewControllerExtension: NSObject
{
}
extension UIViewController
{
func setCornerSettingsTableView()
{
let maskView = UIView()
maskView.frame = self.view.bounds
maskView.backgroundColor = UIColor.lightGrayColor()
let tableView = self.storyboard?.instantiateViewControllerWithIdentifier("cornerSettingVC") as! cornerSettingVC
addChildViewController(tableView)
tableView.view.frame = CGRectMake(maskView.frame.width-maskView.frame.width/2.5, 0,self.view.frame.width/2.5, self.view.frame.height-200)
maskView.addSubview(tableView.view)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissMaskView")
maskView.addGestureRecognizer(tap)
self.view.addSubview(maskView)
}
func dismissMaskView()
{
print("dismiss called")//function called but how to dismiss the mask View
}
}
Usage - In any view controller where I need to display i just call - setCornerSettingsTableView()
Problem - As you can see I am trying to add a tap gesture recognizer to my mask view so that whenever user taps the mask view, it removes the mask view along with the table view in it, but I am unable to achieve that.
If any alternative suggestions for this are most welcome.
extension UIViewController
{
func setCornerSettingsTableView()
{
if let theMask = self.view.viewWithTag(666) as? UIView {
return // already setted..
} else {
let maskView = UIView()
maskView.tag = 666
maskView.frame = self.view.bounds
maskView.backgroundColor = UIColor.lightGrayColor()
let tableView = self.storyboard?.instantiateViewControllerWithIdentifier("cornerSettingVC") as! cornerSettingVC
addChildViewController(tableView)
tableView.view.frame = CGRectMake(maskView.frame.width-maskView.frame.width/2.5, 0,self.view.frame.width/2.5, self.view.frame.height-200)
maskView.addSubview(tableView.view)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissMaskView")
maskView.addGestureRecognizer(tap)
self.view.addSubview(maskView)
}
}
func dismissMaskView()
{
print("dismiss called")//function called but how to dismiss the mask View
if let theMask = self.view.viewWithTag(666) as? UIView {
theMask.removeFromSuperview()
}
}
}

Resources