UIScrollViewDelegate methods not working with a UITextView subclass - objective-c

I have a class MyTextView which inherits from UITextView. I also have a MyViewController which is a subclass of UIViewController. MyViewController implements the UITextViewDelegate protocol and is set as the delegate of MyTextView.
MyViewController implements some of the delegate methods from the UITextViewDelegate (like - (void)textViewDidChange:(UITextView *)textView) and they all work fine. However, when I try to implement - (void)scrollViewDidScroll:(UIScrollView *)scrollView or some or the other scroll delegate methods they do not get called. It is my understanding that this should work since UITextView inherits from UIScrollView and UITextViewDelegate conforms to UIScrollViewDelegate which declares the scroll methods in question.
The strange thing is that if I go into IB and change the class of the text view from MyTextView to UITextView all the delegate methods get called, including the scrolling onces.

It's possible you re-synthesized the delegate property, which will mean there is now a new instance variable private to your class, and synthesized accessors pointing to that private variable. So any superclass implementations that refer to the delegate by instance variable rather than the property will not get through (those referring to the property will invoke your subclasses accessors still, of course). Looks like maybe UITextField uses the property and UIScrollView uses the instance!

I have not tried this, but it might have something to do with protocols?
In that case, the third answer might help:
How to use custom delegates in Objective-C

Related

setHidden with NSSlider doesn't work - Objective C

Hy guys, I'm new at ObjC and I'm still learning;
[sliderContrast setHidden:YES] (i also used slider.hidden = YES) doesn't make the slider invisible, instead it works fine with textfields. Do you know why?
I've also tried using property and synthesize but the result doesn't change
---Interface
#interface Controller : NSWindowController{
IBOutlet NSTextField *labelContrast;
IBOutlet NSTextField *valueContrast;
IBOutlet NSSlider *sliderContrast;
}
- (IBAction)changeContrast:(id)sender;
#end
---Implementation
#import "Controller.h"
#interface Controller ()
#end
#implementation Controller
- (void)windowDidLoad {
[super windowDidLoad];
[labelContrast setHidden:YES];
[valueContrast setHidden:YES];
[sliderContrast setHidden:YES];
}
- (IBAction)changeContrast:(id)sender {
}
#end
If you declare pointers for your objects but you don't allocate them yourself you can not set anything that is not there. Your setHidden: method calls end up in local void aka nowhere.
programmatically
If you go the coding way you would declare, allocate and initiate first. With
labelContrast = [NSTextField alloc] initWithFrame:NSMakeRect(x,y,w,h)];
before you call other methods of the object class (Or similar init methods).After that you can call methods on the object.
Almost all objects inherit an according -(instancetype)init or -(instancetype)initWith... method you can use. If there is no init method given, then there is another way to do it right and the moment to start reading again :).
With Interface Builder
By typing IBOutlet or IBAction in front of a declaration you just give a hint for Xcodes Interface Builder where to hook up and apply onto (associate) the placed object in (nib,xib,storyboard) with its object ID in the XML scheme to a reference in code.
So after you connected your code and the object in IB you can avoid allocation and init process for that particular object. Be aware that calling methods on objects that are not instanced yet is not working. Which is why you code in - (void)windowDidLoad, -(void)viewDidLoad or -(void)awakeFromNib because those are the methods that get called after "IB" has done its job for you.

weak delegate becomes nil before the delegation event method called

I have some heavy processing tasks in my view controller, so I put them in a background thread using GCD. And the tasks are encapsulated in a class AsyncImageProcessor
#interface AsyncProcessor : NSObject
#property (weak, nonatomic) id<AsyncProcessorDelegate> delegate;
// ... other unrelated stuff
#end
#protocol AsyncProcessorDelegate <NSObject>
- (void) asyncProcessorEvent:(AsyncProcessorEventType) event;
#end
The problem is that the view controller implementing the delegate is dismissed right after the AsyncProsessor tasks dispatched. And by the time the processing is done, the view controller memory is released, delegate is nil. Then delegated event can't be passed back to the view controller.
One solution I can think of is to define the delegate "strong", and in the delegated method asyncProcessorEvent, set delegate to nil to break the memory circle after all the processing is done. But I feel this is a hack, and I was told everywhere the delegate should be defined as "weak".
Has someone had this issue before? Please give some suggestions.

Creating Delegate in UIScrollView Subclass

I have subclassed a UIScrollView to customize it a bit. I am trying to create a delegate that will notify several other classes that a user has done a certain thing in the UIScrollView. In my UIScrollView class I have the code below. The problem I am running into is I am getting the warning
Property 'delegate' 'retain (or strong)' attribute does not match the
property inherited from 'UIScrollView'
I see that this is because my Class in inheriting from UIScrollView, but my delegate is conforming to the NSObject. This is the first time I tried creating my own delegate. What can I do to fix this?
My Code:
#import <UIKit/UIKit.h>
#protocol ChangeSpaceDelegate <NSObject>
- (void)changeSpace:(int)spaceId;
#end
#interface CustomUIScrollView : UIScrollView {
id<ChangeSpaceDelegate> delegate;
}
#property (retain, nonatomic)id delegate;
#end
To answer your question specifically, you are redefining the property attribute on the delegate property you get from UIScrollView. It should, like all delegates, be weak (or, pre-iOS 5, unsafe_unretained).
However, you shouldn't do it this way. UIScrollView already has a delegate, and if you expect to put your own delegate object implementing your new delegate methods into it, the inner workings of UIScrollView aren't going to be happy. Define a new protocol and a new delegate property for it.
#property (weak, nonatomic) id<ChangeSpaceDelegate> changeSpaceDelegate;
You don't have to create the delegate object in custom scrollview class since you are subclassing it from UIScrollView. You can directly use it as self.delegate in your custom scrollview class.
As mentioned by #Steve Madsen, I often add own delegate properties for subclasses. Like UITableView has separate DataSource and Delegate properties, and being assigned with the same object. In a long run, this will definitely pay off by not forcing you to repeat what have been already implemented in super class, and keeping your subclass implementations more manageable

Using a protocol and a delegate

I am trying to get some code in a view working. I have declared a delegate and it does not get instantiated, any Idea what I am missing?
I have tried to summarise how I have done this below. I think that the issue is that somewhere, my dataSource delegate needs to be instantiated.
I have a View called graph view and a delegate that is in the viewcontroller GraphViewController.
I know that the method in GraphView is doing something as it calls the AxisDrawing helper class and draws in Axes.
Here is the relevant code
In GraphView.h I set up the protocol and the dataSourceDelegate
#class GraphView;
#protocol GraphViewDataSourceDelegate
- (float)functionOfX:(float)xValue inGraphView:(GraphView *)sender;
#end
#interface GraphView : UIView
#property(nonatomic, weak) IBOutlet id <GraphViewDataSourceDelegate> dataSourceDelegate;
#end
Which I synthesize in GraphView.m
#import "GraphView.h"
#import "AxesDrawer.h"
#implementation GraphView
#synthesize dataSourceDelegate = _dataSourceDelegate;
Also in Graph View I try to use the delegate as follows (pixel is a CGPoint). This routine does run and I can draw to GraphView from here provided I do not try to use my protocol method. i.e. Some of my DrawRect stuff does get drawn which checks out the linking of the UIView to my custom View
pixel.y = [self.dataSourceDelegate functionOfX:pixel.x inGraphView:self];
++++Breakpoint put in here+++++
In GraphViewController I state that I implement the protocol and implement it as follows. The compiler warns me and spots when the implementation is done, turning of the warning. (I am only returning 3.0 at the moment as a test).
#interface
GraphViewController () <GraphViewDataSourceDelegate>
#property (nonatomic, weak) IBOutlet GraphView *graphView;
#end
...
-(float) functionOfX:(float)xValue inGraphView:(GraphView *)sender{
NSLog(#"fofx accessed");
return 3.0;
}
If I look at the GraphView* object just after a breakpoint, it seems to have no instance. What am I missing out.
This is from the trace at the breakpoint
_dataSourceDelegate struct objc_object * 0x0
EDIT: (In addition to #Clays answer below)
It turned out that my connection to the custom view was broke. This meant that the fault lay with the View not talking to the Custom ViewController. The link is made by ctrl dragging from the View Controller button in the Interface builder to the View within the ViewController.
This caused the ViewController to be instantiated and everything then worked fine.
To put it another way.
The link in question was the Outlet. This has to be declared in the ViewController as a property and then linked in IB using ctrl Drag from the ViewController Name-Bar to the View in the Controller.
Provided that you have added the Outlet property correctly, when you do the ctrl drag, your view will appear an option.
The context button popup information on the ViewController button in IB does give a clue.
If you have neglected to put the outlet property into the view controller, the link does appear in the context button popup but it is greyed out, and when you do the link your View is not named.
Once you put the outlet in, the name appears in the menu but not greyed out.
Make sure you've hooked everything up correctly: setting your GraphView's dataSourceDelegate, and your GraphViewController's graphView.
From the trace it looks like you haven't done that; or you lose the reference somewhere along the way because at some point nothing's holding on to it.

Get subView's containing view

If I have a view (mainView) in which I have added subViews. How do I get to subviews instance to mainView ?
I have a button in those subviews which when pressed should call a method in mainView, so I tried:
[myButton addTarget:self.presentingViewController
action:#selector(myMethod:)
forControlerEvents:UIControletcetc];
and
[myButton addTarget:self.parentViewController action:#selector....];
I read that parentViewController now returns nil in iOS 5, but presentingViewController doesn't seem the way to do it because its not presented modally. Its just a subview. Any hints?
Your question doesn't make it quite clear whether you are talking about views or view controllers. Use the superview property to access a view's parent.
There is generally no way to get from a view to its view controller.
It feels to me that you're trying to let your views know about objects beyond their scope.
If you need to notify about an event to anyone that included your view, you could use a delegate to do that.
Say:
#protocol YourClassDelegate
#optional
- (void)instanceOfYourClass:(YourClass *)instance tappedButton:(UIButton *)button;
#end
and then
#interface YourClass
#property (nonatomic, assign) id<YourClassDelegate> delegate;
#end
I hope you can take it from here, by synthesizing the property, assigning the delegate and calling the method when you need to.