I have an UIView class that contains a UILabel. I have added a UITapGestureRecognizer for the UILabel and want to do a performSegue to open a new UIViewController on tap of the UILabel.
The problem is that I can't use performSegue in UIView class.
Can anyone please help to use performSegue in UIView class?
I have added a tap gesture to my label as,
nameLabel.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
tap.numberOfTapsRequired = 1
tap.numberOfTouchesRequired = 1
tap.isEnabled = true
nameLabel.addGestureRecognizer(tap)
Now in my tapFunction,
func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
self.performSegue(withIdentifier: "memberSegue", sender: self)
}
But I get an error:
"Value of type UIViewController has no member performSegue"
as it is an UIView Class.
You should try and separate any functionality from your UIViews. A best practice is your UIViewControllers to be responsible for any functionality in your app and your UIViews to be used only for displaying elements. Thus I would suggest that you make your nameLabel a property in your UIView, so that you can access it from your UIViewController after instantiating the UIView. Then the code in your UIViewController should look like this:
let yourView = new YourView()
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
tap.numberOfTapsRequired = 1
tap.numberOfTouchesRequired = 1
tap.isEnabled = true
yourView.nameLabel.addGestureRecognizer(tap)
and you would have your tap() function in your UIViewController.
you can do it by protocol and delegate. Because actually you can not do it from UIView you should always do it from controller.
/// add this protocol in your project
protocol ViewProtocol {
func performSegueFromView()
}
class View : UIView {
/// add this line to your view
var delegate : ViewProtocol?
/// replace your tap function with this
func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
self.delegate?.performSegueFromView()
}
}
class ViewController : UIViewController, ViewProtocol {
override func viewDidLoad() {
/// create view
let view = View()
/// assign view delegate
view.delegate = self
}
/// delegate function
func performSegueFromView() {
///peform segue from controller because you can not do this from uiview anyway
self.performSegue(withIdentifier: "memberSegue", sender: self)
}
}
Make delegate in UIView Class and used to where you are using UIView in YourController class
protocol UIViewDelegate {
func tapFunction() //this function will be use in controller class which uiview are using
}
var delegate: UIViewDelegate?
func tapFunction(sender:UITapGestureRecognizer) {
delegate?.tapFunction()
}
class YourController: UIViewController, UIViewDelegate {
let yourView = YourView()
yourView.delegate = self
func tapFunction() {
self.performSegue(withIdentifier: "memberSegue", sender: self)
}
}
I would suggest you to add action for that tap gesture instead of segue.
TapRecognizer.addTarget(self, action: #selector(YourFunc))
Samle Func:
func YourFunc(){
let storyBoardHome = UIStoryboard(name: "", bundle: nil)
let memberController = storyBoardHome.instantiateViewController(withIdentifier: "")
self.navigationController?.pushViewController(memberController, animated: true)
}
Refer This : https://stackoverflow.com/a/26366366/6818278
the error you wrote is wrong, UIViewController's do have that method. Your error must be "UIVIew has no member...", as for the solution you just need to get a reference to whatever UIViewController is using this specific view.
Then use that reference to call your perform method.
For example, if your custom View is being added programmatically, you can add an extra property to your class like
weak var myVC : UIViewController?
then after instantiating it just assign it to this property.
Then the perform segue becomes:
myVC?.performSegue....
Alternatively you can just add the tap gesture recognizer from the UIViewController that is using this view, then have the view show it from there.
Or you could just use:
UIApplication.shared.keyWindow?.rootViewController.performSegue...
But if you do, you should be careful as this one might have unintended consequences depending on what you are presenting and which is the current rootViewController.
Related
I have some buttons in Child View Controller. I'd like a user to be able to click on these buttons, and, in case he clicks in other area, Parent View Controller to be able to process those clicks that Child View Controller doesn't work with.
You need to create protocols and delegates.
protocol Vc2Delegate : AnyObject {
func buttonPressed()
}
class Vc1 : UIViewController, Vc2Delegate {
func presentVC2() {
let vc2 = Vc2()
vc2.delegate = self
present(vc2)
}
func buttonPressed() {
// process vc2 button press in vc1
}
}
class Vc2 : UIViewConttoller {
// weak !!!!
weak var delegate : Vc2Delegate? = nil
func buttonInVc2Pressed() {
// call func in vc1 from vc2
delegate?.buttonPressed()
}
}
P.S. Sorry for formatting, was wrote from iPhone.
In my project I have UINavigationController with three embedded UIViewControllers. On the first one I add balanceLabel and refreshButton to navigation bar. When click on button first view controller send url request and show return value on label.
#IBAction func refreshButtonAction(_ sender: UIBarButtonItem) {
let operation = GetInfoOperation(...)
operation.completionBlock = { [weak self] in
DispatchQueue.main.async {
guard let balance = operation.output?.value?.balance else { return }
self?.balanceLabel.text = balance
let significantDigits = Int(Double(balance.toInt64!) * pow(10, -10))
}
}
queue.addOperation(operation)
}
How can I get the same behaviour on other ViewControllers without duplicate #IBAction func refreshButtonAction(_ sender: UIBarButtonItem) in each ViewController?
you can archive this by extension, using inheritance,
create the view controller you want with all the common feature you want and then instead of inheriting directly from UIViewController inherit from that base viewController
your base controller BaseViewController
class BaseViewController: UIViewController {
//all comman functionallity
//add your button here
}
class ChildOneViewController: BaseViewController {
//this class will get all the functionality from BaseViewController
//you can access the BaseViewController button here
//add function for ChildOneViewController
}
class ChildtwoViewController: BaseViewController {
//this class will get all the functionality from BaseViewController
//you can access the BaseViewController button here
//add function for ChildtwoViewController
}
At the moment I have a ViewController class containing a UIScrollView, within the scroll view I have another view controller, where I can currently receive gesture recognition. My goal is to be able to perform a segue to a different view controller, based on which subViewController I tap.
let scrollView = UIScrollView(frame: CGRect(x:0,y:0, width: self.view.frame.width, height:self.view.frame.height-106))
scrollView.delegate = self;
self.view.addSubview(scrollView);
let subView11 = subView(nibName: nil, bundle: nil);
subView1.view.frame = CGRect(x:0,y:0, width: self.view.frame.width, height: CGFloat(openReelHeight));
self.addChildViewController(subView1);
scrollView.addSubview(subView1.view);
subView.didMove(toParentViewController: self);
Then in the subView class I have a basic touch recognition function:
#IBAction func tapOnView(_ sender: UITapGestureRecognizer) {
//change main View controller
}
I would suggest letting the parent perform the segue. So you need a mechanism to let the child inform the parent that the button has been tapped. Here are two approaches:
The child view controller can define a protocol and then have its #IBAction for the button invoke that in the parent view controller.
protocol ChildViewControllerDelegate {
func child(_ child: ChildViewController, didTapButton button: Any)
}
class ChildViewController: UIViewController {
#IBAction func didTapButton(_ sender: Any) {
if let parent = parent as? ChildViewControllerDelegate {
parent.child(self, didTapButton: sender)
}
}
}
Clearly, the parent view controller needs to conform to that protocol:
extension ViewController: ChildViewControllerDelegate {
func child(_ child: ChildViewController, didTapButton button: Any) {
// now segue to whatever you want
}
}
You can alternatively follow an explicit protocol-delegate pattern, rather than relying upon the view controller containment relationships of the parent:
protocol ChildViewControllerDelegate: class {
func didTapButton(_ sender: Any)
}
class ChildViewController: UIViewController {
weak var delegate: ChildViewControllerDelegate?
#IBAction func didTapButton(_ sender: Any) {
delegate?.didTapButton(sender)
}
}
And then, when the parent adds the child, it would have to explicitly set the delegate:
let child = storyboard!.instantiateViewController(withIdentifier: "ChildViewController") as! ChildViewController
addChildViewController(child)
child.delegate = self
// add the child's view to your view hierarchy however appropriate for your app
child.didMove(toParentViewController: self)
And, of course, the parent again has to conform to this protocol:
extension ViewController: ChildViewControllerDelegate {
func didTapButton(_ sender: Any) {
// segue to next scene
}
}
Note, with both of these approaches, you can change your protocol's func to include whatever parameters you want (e.g. passing back contents of some UITextField or whatever). Likewise, you might use method names that make the functional intent of the child a little more explicit. I used somewhat generic method and protocol names because I don't know what the various children are doing.
I have a UIViewController in which I added a UIView. In that UIView I added some UIImageViews through programming. What I need is that when I click the image it shows in a new UIViewController.
ViewController : UIViewController {
myUIview[[imageview] [ ] [ ] [ ] [ ] ]
}
If I understand correctly you want to segue to another ViewController when your users press on an UIImageView. So here is my solution using an UITapGestureRecognizer (put this code in viewDidLoad):
yourImageView.isUserInteractionEnabled = true
let yourImageTapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.test(_ :)))
yourImageTapGesture.delegate = self
yourImageTapGesture.numberOfTapsRequired = 1
yourImageView.addGestureRecognizer(yourImageTapGesture)
Then in your ViewController you have to make the function test, which is triggered when somebody taps on your UIImageView:
func test(_ sender: AnyObject) {
self.performSegue(withIdentifier: "YourSegueName",sender: self)
}
Important: "YourSegueName" is the Identifier of your Segue in your Storyboard. You can set it by clicking on your Segue:
Keep in my mind that your ViewController needs to inherit from one more class:
class ViewController: UIViewController, UIGestureRecognizerDelegate {...}
Basically, I have a rectangle, and I want the Photos Library to open when the user touches the rectangle.
I tried instantiating the viewcontroller as an object in my view class, but was not very successful as my openPhotoLibrary() never runs.
Code that may be helpful.
Edited
I tried delegates, but am still struggling.
View Class
protocol PhotoLibraryDelegate {
func openPhotoLibrary(sender: ImagePipView)
}
class ImagePipView: BasePipView{
var delegate: PhotoLibraryDelegate?
var addPhotoFrame = UIView(frame: CGRectMake(frame.width/2 - 30, 60, 60, 60))
addPhotoFrame.backgroundColor = UIColor.blueColor()
addPhotoFrame.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "addPhotoTouched:"))
addSubview(addPhotoFrame)
func addPhotoTouched(recognizer: UITapGestureRecognizer){
println("PHOTO TOUCHED")
delegate?.openPhotoLibrary(self)
}
ViewController class
class CameraViewControllerTest: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, PhotoLibraryDelegate{
init() {
super.init(nibName:"MyNib", bundle:nil)
var productRequest: ImagePipView?
productRequest?.delegate = self
}
func openPhotoLibrary(){
println ("inside photo library")
var photoPicker = UIImagePickerController()
photoPicker.delegate = self
photoPicker.sourceType = .PhotoLibrary
self.presentViewController(photoPicker, animated: true, completion: nil)
}
You'll need to create a delegate between your controller and your UIView, in the same manner than the UITableViewDelegate. Create a delegate instance in your view, and from your controller, when instantiating your view, set myView.delegate = self. You just have to implement the protocol in your controller then.
You can find a tutorial on delegation in Swift here.
You cannot access controller in view. Instead in your controller add the tap gesture to your view, thus your controller has a reference to your ImagePipView instance.