I have an UIViewController
class WelcomeViewController: UIViewController
and an UIView
class SignUpView: UIView
Now I want to set in my WelcomeViewController delegate of SignUpView:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class SignUpView: UIView { ... }
and connect it in
class WelcomeViewController: UIViewController, SegueDelegate {
how can I set in my WelcomeViiewController those delegate? When I'm trying to set:
override func viewDidLoad() {
SignUpView.delegate = self
}
it returns me
Instance member 'delegate' cannot be used on type 'SignUpView'
how can I find a solution?
You are trying to set delegate to a class. It should be an instance of the class i.e
let signUpView = SignUpView()
signUpView.delegate = self
What would be the point in doing that? If you want to navigate from one View to another, just add that Segue in Storyboard with an Identifier, so you can call self.performSegueWithIdentifier("IdentifierOfSegue", sender: self)
Create a weak property in SignUpView of that delegate(protocol) and name it other than delegate
then you can set and use it.
I agree with the developers saying "you can just do that via segue" but
the problem is you didn't declare a delegate var in the SignUpView class
so you can implement it in the signIn , if you declared it please write the line of code for me in a comment to check it
for now ...
I can suggest that you make a subview to be a parent class then override
which method you want to call
and you need to declare the delegate var as an optional (so you won't have
a memory cycle) like the following line ...
var delegate: SegueDelegate?
Let's solve this for people in need whom could need a solution when reading this issue:
In your UIView:
class SignUpView: UIView
you need to add:
var delegate : SegueDelegate?
Now, still in your class SignUpView, you need to add the function you want to delegate, just like this:
func runSegue(identifier: String) {
delegate?.runSegue(identifier)
}
This will call your delegate:
protocol SegueDelegate {
func runSegue(identifier: String)
}
Now, in your ViewController, you should have your SignUpView somewhere (created programmatically or linked through Storyboard / XIB).
In your viewDidLoadfunction, add: signUpView.delegate = self.
Don't forget to add SegueDelegatein your class heritage.
Related
I needed to delegate a click action for my UIView class to my UIViewController class since Swift does not support multiple class inheritance. So i wanted it such that once a button is clicked on my subview, a function in my BrowserViewController class is called.
I am using a protocol to achieve this, but on the function does not triggered when the button is tapped. Please help me out.
View Controller
class BrowseViewController: UIViewController {
var categoryItem: CategoryItem! = CategoryItem() //Category Item
private func setupExplore() {
//assign delegate of category item to controller
self.categoryItem.delegate = self
}
}
// delegate function to be called
extension BrowseViewController: ExploreDelegate {
func categoryClicked(category: ProductCategory) {
print("clicked")
let categoryView = ProductByCategoryView()
categoryView.category = category
categoryView.modalPresentationStyle = .overCurrentContext
self.navigationController?.pushViewController(categoryView, animated: true)
}
}
Explore.swift (subview)
import UIKit
protocol ExploreDelegate: UIViewController {
func categoryClicked(category: ProductCategory)
}
class Explore: UIView {
var delegate: ExploreDelegate?
class CategoryItem: UIView {
var delegate: ExploreDelegate?
var category: ProductCategory? {
didSet {
self.configure()
}
}
var tapped: ((_ category: ProductCategory?) -> Void)?
func configure() {
self.layer.cornerRadius = 6
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.categoryTapped)))
self.layoutIfNeeded()
}
#objc func categoryTapped(_ sender: UIGestureRecognizer) {
delegate?.categoryClicked(category: ProductCategory.everything)
self.tapped?(self.category)
}
}
}
Simply add a print statement inside categoryTapped.
You will then know if it is actually being tapped.
A million things could go wrong, for example, you may have forget to set the UIView to allow intertaction.
After checking that. Next add another print statement inside categoryTapped which shows you whether or not the delegate variable is null.
You'll quickly discover the problem using simple print statements.
print("I got to here!")
It's that easy.
And what about
if delegate == nil { print("it is nil!! oh no!" }
else { print("phew. it is NOT nil.") }
Debugging is really that easy at this level.
Next add a print statement inside setupExplore()
func setupExplore() {
print("setup explore was called")
....
See what happens.
I don't see any piece of code which sets the delegate.
First of all, define delegate as a property inside CategoryItem class, Then you must set the current instance of BrowseViewController to the delegate variable of CategoryItem. Now you can expect your method being called.
There are a few things that could cause the delegate method to not be triggered in this code:
Ensure that isUserInteractionEnabled = true on your CategoryItem. This is probably best done in either the configure() function in the CategoryItem or in the setupExplore() function of the BrowseViewController.
Make sure that the setupExplore() function on the BrowseViewController is being called, and that the category is being set on the CategoryItem to trigger the configure function. Otherwise, either the delegate or the gesture recognizer might not being set.
Side Note - weak vs strong delegate
On a side note, it is usually best practice to make your delegate properties weak var rather that having them be a strong reference, as this makes them prone to strong retain cycles.
Therefore, you might want to consider making the var delegate: ExploreDelegate? on your CategoryItem into weak var delegate: ExploreDelegate?. For more information on this problem, view this post.
I have created a custom UIView and have a protocol set for it. Now from the View Controller when I set the delegate to self I am getting an EXC_BAD_ACCESS.
---The View Controller Code------
class VerificationController: UIViewController, LoadingViewDelegate {
#IBOutlet weak var instructionView: LoadingView!
override func viewDidLoad() {
super.viewDidLoad()
instructionView.randomTextIndexes = [1]
instructionView.delegate = self
}
...
}
// "instructionView" is the UIView outlet and "LoadingView" is the class
--This is the custom View Code ------
protocol LoadingViewDelegate {
func generated(random code:String)
}
class LoadingView: UIView {
var delegate:LoadingViewDelegate?
var randomTextIndexes:[Int] = []
}
I am getting EXC_BAD_ACCESS when I try to access the delegate as well as the randomTextIndexes from the viewDidLoad() method of the view controller. Could you please tell me what I am missing here.
My best guess is that you forgot to set the class of the custom view in the Interface Builder. Check the Identity Inspector for your object in the IB, it should look like this:
If it isn't set, then that's the problem.
I'm trying to create a reusable UIView that I can place in multiple UIViewControllers. I gave it delegate methods that I want the parent UIViewControllers to access, but it throws me an error (commented in the code below). What's a better way I can solve this?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var cameraView: CameraView!
override func viewDidLoad() {
super.viewDidLoad()
self.cameraView.delegate = self
//ERROR: Cannot assign a value of type 'viewController' to a value of type 'CameraViewDelegate?'
}
}
protocol CameraViewDelegate {
func cameraViewShutterButtonTapped()
func cameraViewimagePickerTapped(imageData: NSData)
}
class CameraView: UIView {
var delegate:CameraViewDelegate?
//Ect...
}
You have not specified that ViewController conforms to the CameraViewDelegate protocol. You should amend your code to this:
class ViewController: UIViewController, CameraViewDelegate {
…at which point Xcode will complain that you have not implemented cameraViewShutterButtonTapped() and cameraViewimagePickerTapped(), which at least tells you that you're on the right track!
Side note: do you really want the camera view to have a strong reference to its delegate? You might want that to be weak.
You need to have your ViewController class implement the CameraViewDelegate protocol, like so:
class ViewController : UIViewController, CameraViewDelegate { ... }
All of the searches I've done focus on passing data between view controllers. That's not really what I'm trying to do. I have a ViewController that has multiple Views in it. The ViewController has a slider which works fine:
var throttleSetting = Float()
#IBAction func changeThrottleSetting(sender: UISlider)
{
throttleSetting = sender.value
}
Then, in one of the Views contained in that same ViewController, I have a basic line that (for now) sets an initial value which is used later in the DrawRect portion of the code:
var RPMPointerAngle: CGFloat {
var angle: CGFloat = 2.0
return angle
}
What I want to do is have the slider's value from the ViewController be passed to the View contained in the ViewController to allow the drawRect to be dynamic.
Thanks for your help!
EDIT: Sorry, when I created this answer I was having ViewControllers in mind. A much easier way would be to create a method in SomeView and talk directly to it.
Example:
class MainViewController: UIViewController {
var view1: SomeView!
var view2: SomeView!
override func viewDidLoad() {
super.viewDidLoad()
// Create the views here
view1 = SomeView()
view2 = SomeView()
view.addSubview(view1)
view.addSubview(view2)
}
#IBAction func someAction(sender: UIButton) {
view1.changeString("blabla")
}
}
class SomeView: UIView {
var someString: String?
func changeString(someText: String) {
someString = someText
}
}
Delegate:
First you create a protocol:
protocol NameOfDelegate: class { // ": class" isn't mandatory, but it is when you want to set the delegate property to weak
func someFunction() // this function has to be implemented in your MainViewController so it can access the properties and other methods in there
}
In your Views you have to add:
class SomeView: UIView, NameOfDelegate {
// your code
func someFunction() {
// change your slider settings
}
}
And the last step, you'll have to add a property of the delegate, so you can "talk" to it. Personally I imagine this property to be a gate of some sort, between the two classes so they can talk to each other.
class MainViewController: UIViewController {
weak var delegate: NameOfDelegate?
#IBAction func button(sender: UIButton) {
if delegate != nil {
let someString = delegate.someFunction()
}
}
}
I used a button here just to show how you could use the delegate. Just replace it with your slider to change the properties of your Views
EDIT: One thing I forgot to mention is, you'll somehow need to assign SomeView as the delegate. But like I said, I don't know how you're creating the views etc so I can't help you with that.
In the MVC model views can't communicate directly with each other.
There is always a view controller who manages the views. The views are just like the controllers minions.
All communication goes via a view controller.
If you want to react to some view changing, you can setup an IBAction. In the method you can then change your other view to which you might have an IBOutlet.
So in your example you might have an IBAction for the slider changing it's value (as in your original question) from which you could set some public properties on the view you would like to change. If necessary you could also call setNeedsDisplay() on the target view to make it redraw itself.
UIScrollView has a delegate property which conforms to UIScrollViewDelegate
protocol UIScrollViewDelegate : NSObjectProtocol {
//...
}
class UIScrollView : UIView, NSCoding {
unowned(unsafe) var delegate: UIScrollViewDelegate?
//...
}
UICollectionView overrides this property with a different type UICollectionViewDelegate
protocol UICollectionViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class UICollectionView : UIScrollView {
unowned(unsafe) var delegate: UICollectionViewDelegate?
//...
}
When I try to override UIScrollViews delegate with my protocol like so:
protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class MyScrollView: UIScrollView {
unowned(unsafe) var delegate: MyScrollViewDelegate?
}
the compiler gives me two warnings:
Property 'delegate' with type 'MyScrollViewDelegate?' cannot override a property with type 'UIScrollViewDelegate?'
'unowned' cannot be applied to non-class type 'MyScrollViewDelegate?'
How can I subclass UIScrollView and override type of delegate property (i.e. use a custom delegate protocol) ?
I think overriding an inherited property is something that's possible in Objective-C but not (at least currently) in Swift. The way I've handled this is to declare a separate delegate as a computed property of the correct type that gets and sets the actual delegate:
#objc protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
func myHeight() -> CGFloat
// ...
}
class MyScrollView: UIScrollView {
var myDelegate: MyScrollViewDelegate? {
get { return self.delegate as? MyScrollViewDelegate }
set { self.delegate = newValue }
}
}
This way anything that calls the scroll view delegate normally still works, and you can call your particular delegate methods on self.myDelegate, like this:
if let height = self.myDelegate?.myHeight() {
// ...
}
You can do like this:
protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var myDelegate: ExtendedScrollViewDelegate?
override weak var delegate: UIScrollViewDelegate? {
didSet {
myDelegate = delegate as? ExtendedScrollViewDelegate
}
}
}
Hope this helps
My favoured method personally is not to subclass scrollviews directly but to make a UIView subclass containing and acting as delegate for a separate scrollview, then forward that scrollview's delegate messages on to the UIView subclass's own delegate where necessary. This also allows for the adding of custom controls outside of the area defined by the scroll view. It may seem a little inelegant compared to a direct subclass, but it does at least avoid unpleasant hacks.
Here is a solution for changing the type of the overriding properties in Swift. It is especially useful when you need to extend protocols of delegates.
#objc protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var delegateInterceptor: ExtendedScrollViewDelegate?
override var delegate: UIScrollViewDelegate! {
didSet {
if let newValue = delegate {
let castedDelegate = unsafeBitCast(delegate, ExtendedScrollViewDelegate.self)
delegateInterceptor = castedDelegate
}
else {
delegateInterceptor = nil
}
}
}
}
This works as tested with Swift version 1.2. I hope it helps.
You can override get and set method by declare function like:
func setDelegate(delegate:UITableViewDelegate?){
self.delegateInterceptor = delegate;
}
swift compiler the property to method as Objective-c does.
Consider the following situation:
class BaseProp {}
class Base {
var prop: BaseProp
}
Then if you do this:
class DerivedProp: BaseProp {}
class Derived: Base {
override var prop: DerivedProp
}
Then if would break the subclassing principles (namely, the Liskov Substitution Principle). Basically what you are doing is limiting the scope of "var prop" from wider "BaseProp" type to a more narrow "DerivedProp" type. Then this kind of code would be possible, which does not make sense:
class UnrelatedProp: BaseProp {}
let derived = Derived()
let base = derived as Base
base.prop = UnrelatedProp()
Note that we are assigning an instance of UnrelatedProp to the property, which does not make sense for the Derived instance which we actually operate with. ObjectiveC allows such kind of ambiguity, but Swift doesn't.