Objective-C "private" protocols? - objective-c

I got a view controller class (MyViewController) that deals with a UIView subclass (MyView). I don't want to let any class except the view controller class know about the UIView subclass, so I cannot import MyView.h in MyViewController.h.
So, in MyViewController.m, I put
#import "MyViewController.h"
#import "MyView.h"
#interface MyViewController (PrivateObjects)
MyView *myView;
#end
...
However, to get feedback from MyView, I use a delegate. That delegate has to implement the MyViewDelegate protocol.
How can I implement the MyViewDelegate protocol inside MyViewController without having to #import MyView.h in MyViewController.h?

#interface MyViewController (PrivateObjects) <MyViewDelegate>
....
(BTW, you can't declare a new ivar in a category.)

You can use a forware-declaration in MyViewController.h
#class MyView;
#interface MyViewController {
MyView *myView;
}
#end

I know it my sound strange, but by experience I can tell you to don't bother so much about privacy hierarchies in Cocoa.
So in an app or in internal frameworks simply document the class stating how it should used.
That's because you cannot have real privacy as long C pointers are around so Objective-C was designed with no syntax that would only give you some illusion of it.
For example the IB outlet variables of view controllers should typically only be accessible by the controller itself, but they are always public, because they must be accessible from the classes that unarchive and instantiate the nib files and link the outlets with the corresponding instances.
[including suggestions from bbum below]

Create a protocol (.h) outside of MyView.h and use that in the declaration (.h) of MyViewController, for example:
#import "MyViewDelegate.h"
#interface MyViewController : UIViewController <MyViewDelegate>
--Frank

Why do you even want to use a protocol? Just put the methods in your PrivateObjects category.
Edit: Apple refers to this technique as "informal protocol"

Related

Simple passing of data through delegation in objective C

I'm using Xcode to write an app in objective c. I am trying to pass data from a container view controller to the parent view controller using delegation. I have successfully passed the data to the parent view controller, but all of the documentation sets what I have sent to the .h header file in the .m implementation file using viewDidLoad or viewDidAppear. I was wondering, since the view is already present, if there is a way to detect that data has been changed in a view and automatically run a method or code to update the view with the new information. Something along the idea of didReceiveNewData or didEditExistingValues (of course those arent real methods). Thank you for your help!
Edit: What I have done so far:
I want to pass the data from MainFeedTableViewController to MainFeedViewController (The first is in a container inside of the second). I want to set the title of the custom navigation bar in MainFeedViewController to something described in the MainFeedTableViewController.
In the MainFeedTableViewController.m (the view sending data) I have:
#import "MainFeedTableViewController.h"
#import "FeedViewController.h"
#interface MainFeedTableViewController ()
#end
#implementation MainFeedTableViewController
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender {
UIStoryboard *mc = self.storyboard;
FeedViewController *fv = [mc instantiateViewControllerWithIdentifier:#"FeedViewController"];
fv.navigationBarTitleToSet = #"HOPING TO SET TITLE TO THIS";
[self performSegueWithIdentifier:#"MainToLocalFeed" sender:self];
}
and some other unrelated stuff..
In the MainFeedTableViewController.h I have:
#import <UIKit/UIKit.h>
#interface MainFeedTableViewController : UITableViewController
#end
In the MainFeedViewController.m (the one receiving the data) I have:
#import "FeedViewController.h"
#interface FeedViewController () <UINavigationBarDelegate>
#property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
#end
#implementation FeedViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setNavigationBarTitle:(NSString *)navigationBarTitle{
self.navigationItem.title = navigationBarTitle;
}
And in the MainFeedViewController.h I have:
#import <UIKit/UIKit.h>
#interface FeedViewController : UIViewController
#property NSString *navigationBarTitleToSet;
#end
I want to run the setNavigationBarTitle method with either data from the .h (navigationBarTitleToSet) or just from the sending view controller, if possible to run a method with delegation. Thanks a ton and I hope this is possible :)
It turns out I needed to add a second navigation bar to account for the container view, allowing me to navigate around the current stack with the parentViewController method and then navigationItem.title. For anyone who happens to find this with a container, make sure you add one immediately after the embed segue. I'm still not sure if you can use methods through delegation, but I can't ponder any situations where it would be necessary anymore, due to viewDidLoad. Thanks to #Tander for the help!

Objective-c: How to find UITableViewCell's containing UITableView

I've subclassed UITableView with some specific properties that I'd like to be able to access in a subclass of UITableViewCell. Is there something like cell.containingTableView that I'm just missing?
Really you don't want your views to know much of anything about their parents. The thing to do is subclass UITableViewCell, give it some properties to hold the information you want to pass to it and then let your UITableViewController do so in tableView:cellForRowAtIndexPath:
You may think of using
self.superview
but it's VERY fragile, so please DON'T!.
You'd better pass a reference to the UITableView explicitly to the cell.
In your cell declare a property
#property (nonatomic, weak) UITableView *parentTableView;
and assign it in the tableView:cellForRowAtIndexPath: data source method.
Finally if your purpose is just to call some methods on the parent controller, you can use a proper delegation pattern and define your own protocol
#protocol CellDelegate <NSObject>
- (void)aMethodThatINeedToCall:(id)whatever;
#end
declare a delegate property
#property (nonatomic, weak) id<CellDelegate> delegate;
and make your UITableViewController conform to that
#interface MyTableViewController : UITableViewController <CellDelegate>
...
#end
#implementation
...
- (void)aMethodThatINeedToCall:(id)whatever {
// do stuff
}
...
#end

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

How to change the text of a textField from another view

I have this code:
ViewController .h
#property IBOutlet UITextField *field;
ViewController .m
#synthesize field;
ViewControllerTwo .h
#import "ViewController.h"
{
ViewController *ViewCont;
}
-(IBAction)changeTextField
ViewControllerTwo .m
#import "ViewController.h"
-(IBAction)changeTextField{
viewCont.field.text = #"hello";
}
The problem is that it doesn't work, although it doesn't give me any error. Does anyone know what I'm doing wrong?
Never modify another view controller's views. You are encountering one of many problems doing that. In your case, the likely cause is that the other view controller has not yet loaded its view, so all the IBOutlets are still nil.
You're breaking MVC, and that's going to cause lots of little problems like this. Instead of having ViewControllerTwo modify the outlets of ViewController, you should move the data (#"hello") into a model object that is shared by both view controllers. ViewControllerTwo would write to it, and ViewController would read from it. You can share that model object by passing it to the view controllers as a property, or by making the model a singleton.
You aren't instantiating your instance of class ViewController, so you are essentially sending a message to nil.

How to handle cross import?

I've made a new project as a Single View iOS Application in Xcode. I've created a custom class named WebView extending UIWebView. In the storyboard, I'm adding a WebView to the ViewController and then making an IBOutlet to the WebView in the ViewController.h. Instead of using UIWebView class for the IBOutlet, I'm using my cusom WebView class and is importing its header-file in ViewController.h as well. Now my ViewController is connected to the Web VIew of class WebView.
Next, I would like my WebView to have a reference to the UIViewController. I then import the ViewController.h in my WebView.h, but then I start getting some compiler errors like:
Unknown type name 'WebView'; did you mean 'UIWebView'?
I guess the problem is, that ViewController.h imports WebView.h and WebView.h imports ViewController.h. Is it not possible to make cross import in Objective-C?
In WebView.h and ViewController.h, instead of importing each file, you should instead predeclare the needed classes, then do the actual importing inside the .m (implementation) files.
WebView.h
#class ViewController; // This pre-declares ViewController, allowing this header to use pointers to ViewController, but not actually use the contents of ViewController
#interface WebView : UIWebView
{
ViewController* viewController;
}
#end
WebView.m
#import "WebView.h"
#import "ViewController.h" // Gives full access to the ViewController class
#implementation WebView
- (void)doSomething
{
[viewController doSomethingElse];
}
#end
You don't need to import the header to make a simple reference. Instead you can declare the class using
#class WebView;
In the interface, this will be enough for the compiler to create an Outlet. You only need the full header when you want to access properties or methods of the class.