Delegate declared in UIView subclass won't work in UIViewController? - ios

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 { ... }

Related

View Controller only Protocol has no access to View Controller properties

I have a UIViewController only protocol
protocol VCProtocol where Self: UIViewController {}
I have a function with VCProtocol parameter. Inside the function I can not access any property of UIViewController
func testFunction(vcProtocol: VCProtocol) {
// vcProtocol.view ‼️ error: Value of type 'VCProtocol' has no member 'view'
}
Though I can cast the protocol parameter to UIViewController and then access the property like this:
func testFunction(vcProtocol: VCProtocol) {
(vcProtocol as! UIViewController).view
}
Is this is the way? Do we have any better way?
You can use the & operator to combine protocols
protocol VCProtocol where Self: UIViewController {}
func testFunction(vcProtocol: VCProtocol & UIViewController) {
let view = vcProtocol.view
}
It seems like this is now supported properly from Swift 5. You can try it Xcode 10.2 beta 4. For older versions, you would have to resort to #Ricky Mo's solution.
protocol VCProtocol: UIViewController {
func testFunction(vcProtocol: VCProtocol)
}
class A: UIViewController, VCProtocol {
func testFunction(vcProtocol: VCProtocol) {
debugPrint(vcProtocol.view)
}
}
From the notes,
Protocols can now constrain their conforming types to those that
subclass a given class. Two equivalent forms are supported:
protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
Swift 4.2 accepted the second form, but it wasn’t fully implemented
and could sometimes crash at compile time or runtime. (SR-5581)
(38077232)

UIView to UIViewController communication via protocol not working

maybe I am missing something really fundamental here, but after staring at the code for an hour or so, my brain is going trough cycles and I would appreciate a fresh glance at this problem.
I have the following UIView:
import UIKit
protocol DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool)
}
class locationXIBController: UIView {
#IBOutlet weak var loationLabel: UILabel!
#IBOutlet weak var vsedniOteviraciDobaLabel: UILabel!
#IBOutlet weak var prijmajiKartyLabel: UILabel!
#IBOutlet weak var detailViewButtonOutlet: UIButton!
#IBOutlet weak var backgroundViewButton: UIButton!
let openedBool = true
var detailViewWillShowUpDelegate: DetailViewWillShowUpDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func vecerkaDetailButtonPressed(_ sender: UIButton) {
detailViewWillShowUpDelegate?.sendDetailOpened(openedBool)
print("pressed")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = detailViewButtonOutlet.hitTest(convert(point, to: detailViewButtonOutlet), with: event) {
return result
}
return backgroundViewButton.hitTest(convert(point, to: backgroundViewButton), with: event)
}
}
Now the problem is, that when I call/press the vecerkaDetailButtonPressed function I get "pressed" output in the console but the protocol for some reason doesn't go trough.
The other side looks like this (stripped for simplicity):
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationXIB = locationXIBController()
let isVecerkaDetailOpened = false
override func viewDidLoad() {
locationXIB.detailViewWillShowUpDelegate = self
}
extension MapViewController: DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool) {
isVecerkaDetailOpened = openedBool
print("success")
}
}
I know the protocol value at the moment of execution is nil. As I said, any help is appreciated, thanks!
First, a couple of naming convention issues:
The name locationXIBController is a bad choice for a UIView object. It is a view object, not a controller object.
Second, class names in Swift should start with an upper-case letter. So LocationXIBView would be a much better name for that view class.
Next, your code
let locationXIB = locationXIBController()
...is wrong. That creates a brand-new instance of your locationXIBController class that you never install in your view hierarchy. You should make that line an IBOutlet:
#IBOutlet weak var locationXIB: locationXIBController!
And then you should control-drag from the locationXIBController in your StoryBoard onto the outlet in your view controller. That will cause Interface Builder to connect the outlet.
Now when you run your program the variable locationXIB will be connected to the locationXIBController view from your storyboard/XIB when it's loaded.
In addition to the answer of #Duncan C, you might check whether you need super.viewDidLoad() at the top of the viewDidLoad() method in the MapViewController class? Not doing that can lead to quirky things in your app.
I asked:
So does detailViewWillShowUpDelegate actually point at anything, or is it nil?
And you replied:
I just tried debugging and it is actually nil
So that's the problem... you need to set detailViewWillShowUpDelegate to point to a valid delegate object. This is often done in the .xib file or storyboard, and sometimes people forget to make that connection, so check there if it makes sense. Else you'll just need to get a reference to the delegate at some point before the code in question can run and set it up.
Answer to the credit of #Paulw11
I finally managed to get it working by communicating like so:
step 1) 1:1 communication via protocol between MKAnnotation and MKAnnotationView
step 2) 1:1 communication via protocol between MKAnnotationView and MapViewController passing the same data
Finally works like a charm, thanks!

How do you create a base class with a weak delegate that conforms to a generic protocol?

I want to create a base class for UIViews that require that a delegate conform to a specific protocol defined by the View.
class BaseView<P>: UIView {
weak var delegate: P?
}
protocol MyProtocol {}
class MyView: BaseView<MyProtocol> {}
This gives me the error: "'weak' must not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound".
How do I fix this error? Or is there some work around? Or is it not so necessary to make the delegate variable weak in the first place? Thanks in advance.
Since weak is a property assigned to anything that is of class type and not struct, you have to explicitly constraint your generic parameter to be of class type and you do that this way:
class BaseView<P: AnyObject>: UIView {
weak var delegate: P?
}
#objc protocol MyProtocol {
}
class MyView: BaseView<MyProtocol> {
}
Only one need of clarification. Usually to make a protocol be of class type usally you would make it conform to class this way:
protocol MyProtocol: class { }
However, for some reason the compiler throws an error if you were to do it that way. I learned that this is a bug that could be learned about more here:
How to require that a protocol can only be adopted by a specific class
So adding the #objc helps silence the warning and error both.
You should add type constraint to your generic by adding MyProtocol and create a class that conforms MyProtocol.
You can find more info here.
Updated code:
class BaseView<P: MyProtocol>: UIView {
weak var delegate: MyProtocol?
}
protocol MyProtocol: class {}
class MyProtocolImp: MyProtocol {
}
class MyView: BaseView<MyProtocolImp> {
}
But I don't know why you use P parameter in class.
You can write without this:
class BaseView: UIView {
weak var delegate: MyProtocol?
}
protocol MyProtocol: class {}
class MyView: BaseView {
}

How to set UIView delegate?

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.

Overriding delegate property of UIScrollView in Swift (like UICollectionView does)

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.

Resources