Getting two errors in the code with the UIWebViewDelegate - objective-c

I have a problem with this code:
Header:
#interface ViewController : UIViewController {
IBOutlet UITextField *field;
IBOutlet UIWebView <UIWebViewDelegate> *web;
}
Implementation:
#protocol UIWebViewDelegate;
- (void)viewDidLoad{
//some other code here
webView = [[UIWebView alloc] init];
webView.delegate = self;
}
I got two problems. The first is that on the line webView = [[UIWebView alloc] init];</code>the compiler generates the following message:Incompatible pointer types assigning to 'UIWebView*_strong' from 'UIWebView'*'.
The second problem is that I get an error on the line <code>webView.delegate = self;</code> that says:
'Passing ViewController*const_strong to parameter of incompatible type 'id'`.
Any ideas? Any help will be welcome.

You've declared your webView ivar to be a UIWebViewDelegate. You meant to make ViewController a UIWebViewDelegate:
#interface ViewController : UIViewController <UIWebViewDelegate>

Are you using a NIB? Because you are using an IBOutlet in your header file, which would suggest you have a NIB hooked up to it. In which case your web view will already be created, and you don't need the alloc/init call.
Also, why are you doing this:
#protocol UIWebViewDelegate;
...#protocol is used when you want to declare a protocol, not when you want to inidicate a class conforms to it. Perhaps take a quick look over Apple's documentation here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html

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!

Data encapsulation, Instance variable cannot be accessed

I'm having some trouble understanding what classes can read what variables in other classes. I've read to many different things online and cant seem to find anything solid in here. I've literally wasted the past two days trying to get my program to work but no classes can read any other classes variables. Any help will be GREATLY appreciated.
This is my ViewController.h:
#interface ViewController : UIViewController
{
#public
NSString *nameOfLabel;
}
#property (strong, nonatomic) IBOutlet UILabel *firstLabel;
- (IBAction)Switch:(id)sender;
- (IBAction)changeLabel:(UIButton *)sender;
-(NSString *) nameOfLabel;
#end
nameOfLabel is a public variable and should be able to be accessed by an outside class, right?
ViewController.m:
#import "ViewController.h"
#import "NewView.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)Switch:(id)sender {
NewView * new = [[NewView alloc] initWithNibName:nil bundle:nil];
[self presentViewController: new animated:YES completion:NULL];
}
- (IBAction)changeLabel:(UIButton *)sender {
nameOfLabel = #"Test Name";
_firstLabel.text = nameOfLabel;
}
-(NSString *) nameOfLabel {
return nameOfLabel;
}
#end
changeLabel button changes *firstLabel.text to "Test name".
second class is NewView.h:
#import "ViewController.h"
#interface NewView : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *secondLabel;
- (IBAction)changeSecondLabel:(UIButton *)sender;
#end
and NewView.m:
#import "NewView.h"
#interface NewView ()
#end
#implementation NewView
{
ViewController *view;
}
- (IBAction)changeSecondLabel:(UIButton *)sender {
view = [[ViewController alloc] init];
_secondLabel.text = view.nameOfLabel;
}
#end
changeSecondLabel should change secondLabel.text to nameOfLabel which is 'Test name', however, the label actually disappears which makes me think that nameOfLabel cannot be reached. Ive played around with nameOfLabel, making it a #property and then synthesising it, as well as trying putting it in { NSString *nameOfLabel; } under #implementation but I still get the same result.
This line: view = [[ViewController alloc] init]; creates a new ViewController which doesn't know anything about what you may have done to some other ViewController. In your case, it specifically doesn't know that changeLabel: was called on another ViewController before this new one ever existed.
When the second view controller (NewView) is presented, it has no reference to the first view controller (ViewController) and it's data.
Here are a couple of suggestions.
In modern Objective-C I'd recommend using properties instead of exposing a variable.
Look over the naming in general. "ViewController" is not a good name for example.
If the property is part of an internal state of the class, declare it in a class extension.
Before you present the second view controller, set a reference to the string from the first view controller.
Part of ViewController.m:
#interface ViewController ()
#property (copy,nonatomic) NSString *nameOfLabel;
#end
#implementation ViewController
- (IBAction)Switch:(id)sender {
NewView *new = [[NewView alloc] initWithNibName:nil bundle:nil];
new.secondLabel.text = self.nameOfLabel;
[self presentViewController: new animated:YES completion:NULL];
}
First of all please read about coding standards, it's not a good practice to:
Name variables like "new"
Name methods like "Switch"
Name UIViewController like "view" or "NewView"
Regarding logic:
This is all messed up here. What you actually do is you create viewController with nameOfLabel which is empty and is only changed on button press. I assume you press that button so it's changed. Then on switch action you create another viewController and present it. Then from inside that new viewController you create another new viewController which has empty nameOfLabel, get this empty value and put it inside secondLabel.
There are couple of ways you can do to change secondLabel:
Move nameOfLabel to model and read it from there when you want to change secondLabel,
Because your new viewController is child of viewController that keeps nameOfLabel you can access it by calling [[self presentingViewController] nameOfLabel] but make it property first,
Pass nameOfLabel through designated initializer.
Well, if you want a simple demonstration of access of a public ivar, the syntax is:
view->nameOfLabel;
^^
not dot-syntax:
view.nameOfLabel;
(dot-syntax just goes through accessor methods).
I've only seen a handful of warranted edge cases over the years; there's rarely, rarely ever a good reason to make an ivar public (also, protected is also rarely a good choice).

Problem with custom class in Objective-C

I have a little problem with the application I currenty work on. I create a simpliest project to illustrate my problem.
So, I create a "Navigate-Base Application". I add an other UITableViewController named TableViewController (the one which is created with the project is named RootViewController). I create an instance of TableViewController when I touch a line in the RootViewController.
I create a custom class named "MyCustomClass".
MyCustomClass.h (full code) :
#import <Foundation/Foundation.h>
#interface MyCustomClass : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString * name;
#end
MyCustomClass.m (full code) :
#import "MyCustomClass.h"
#implementation MyCustomClass
#dynamic name;
#end
I had a MyCustomClass attibute in TableViewController class.
TableViewController.h (full code) :
#import <UIKit/UIKit.h>
#import "MyCustomClass.h"
#interface TableViewController : UITableViewController {
MyCustomClass *aCustomObject;
}
#property (nonatomic, retain) MyCustomClass *aCustomObject;
#end
At the load of TableViewController, I try to display aCustomObject's content.
TableViewController.m (top of the file and what I modify in the template's file) :
#import "TableViewController.h"
#implementation TableViewController
#synthesize aCustomObject;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSLog(#"Name : %#",self.aCustomObject.name);
}
Before, I create and give a value to aCustomObject.name in RootViewController :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewController *detailViewController = [[TableViewController alloc] initWithNibName:#"TableViewController" bundle:nil];
detailViewController.aCustomObject.name = #"The Name";
[self.navigationController pushViewController:detailViewController animated:YES];
}
Console said :
2011-06-22 07:21:11.087
MyTestApp[12822:207] Name : (null)
I think it's a stupid thing but I don't find myself after hours of try.
Thanks a lot and excuse me for my english mistakes,
You forget to initialize your custom object in the tableViewController's viewDidLoad Method.
Try this.
- (void)viewDidLoad {
[super viewDidLoad];
if(aCustomObject == nil){
self.aCustomObject = [[[MyCustomClass alloc] init] autoRelease];
}
self.aCustomObject.name = #"";
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//this will show empty here.
NSLog(#"Name : %#",self.aCustomObject.name);
}
You use the #dynamic keyword to tell
the compiler that you will fulfill the
API contract implied by a property
either by providing method
implementations directly or at runtime
using other mechanisms such as dynamic
loading of code or dynamic method
resolution. It suppresses the warnings
that the compiler would otherwise
generate if it can’t find suitable
implementations. You should use it
only if you know that the methods will
be available at runtime.
from Apple Documentation
You are claiming in the question that you included full source for MyCustomClass.m. Where did you implement the getter and setter for the property? If you want the compiler to generate the methods for you, you should use
#synthesize name;

Setting the initial value of a UILABEL

I'm trying to create a simple Quiz app (I'm a beginner), when I launch the app I want a UILabel to show the first question (of an array of questions). I'm having some trouble with setting the initial value.
I've done a couple of attempts, whiteout success. I my QuizAppDelegate.h file I declare my UILabel like this:
IBOutlet UILabel * questionField;
In my main .m file I have tried the following:
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
questionField = [[UILabel alloc] init];
[questionField setText:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
Another thing I have tried is the following in QuizAppDelegate:
#property (nonatomic, retain) IBOutlet UILabel *questionField;
- (void)changeTitle:(NSString *)toName;
And in the .m file:
#synthesize questionField;
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
[self changeTitle:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
-(void)changeTitle:(NSString *)toName {
[questionField setText:toName];
}
Any tips on how to solve this would be great!
// Anders
Hopefully you're not actually putting code into main.m. On iOS, you rarely modify that file.
Since you're doing everything in the AppDelegate, let's keep it there (as opposed to creating a new UIViewController). Let's start with the basics.
Adding the Label as an instance variable
You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;
Then, declare the corresponding property, and make sure to synthesize it in the .m file.
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#synthesize questionField // in the .m file
Adding the UILabel in Interface Builder
Open up MainWindow.xib. Drag a UILabel from the Library to the Window that represents your app's window. Then Control-Drag from the AppDelegate object (the third icon on the left in Xcode 4; it'll be labelled in the Document window in IB 3). You'll see a little black window come up—select the option called questionField to make the connection.
See this link for screenshots and how to make connections in IB. The same applies in Xcode 4.
Changing the text
You don't need a separate method to change the text—just modify the label's text property.
Pick a method that'll be called when the app launches (applicationDidFinishLaunching:WithOptions: is a good place to do it in), and put the following code:
questionField.text = #"Hello";
And that's it!
Code
QuizAppDelegate.h
#import <UIKit/UIKit.h>
#interface QuizAppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UILabel *questionField;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#end
QuizAppDelegate.m
#import "QuizAppDelegate.h"
#implementation QuizAppDelegate
#synthesize window=_window;
#synthesize questionField;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
[self.window addSubview:self.questionField];
[self.window makeKeyAndVisible];
self.questionField.text = #"Hello";
return YES;
}
- (void)dealloc
{
[_window release];
[questionField release];
[super dealloc];
}
#end
If you're creating the label programmatically, then you have to add the label to the view:
[self.view addSubview:questionField];
This assumes that you have a ViewController. If not, and you're doing this directly in the AppDelegate (a very bad idea, by the way), then do
[self.window addSubview:questionField];
If you're creating it in the IB, make sure you set up the connections.
You should not both add the UILabel in the IB and instantiate it programmatically. Only call alloc if you are creating it programmatically. Otherwise, if using the IB, skip that part. You created it already with the xib.
I suspect that you have either not created your Interface Builder layout properly - either you have missed the control out all together or more likely you have not connected that control to the questionField outlet in yout header file.
You need to drag a UILabel view into the main view and then connect it to the correct line in your header file.
You shouldn't be using your main.m like that at all. In fact, you should almost certainly never do anything with it. Try creating a UIViewController subclass and practicing your quiz with that. (Add the UILabel to the IB file and then connect the outlet.) Perhaps use the View-Based Application template while you are practicing.
This is a good answer:
"You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;"
I was trying to change the value of mylabel.text and the screen didn't update the label with this.value. I included the {IBOutlet UILabel * mylabel} and it works like a charm!
So this answer is valid to change the text of a label programmatically!
Thanks

How to traverse the class hierarchy through a variable assignment in Cocoa Touch?

I have created a UIViewController class called MyViewController with a UIImageView in its XIB file. I then import this class into another class. I make an instance of the class and I change the image in the UIImageView using code:
myViewController.myImageView.image = [UIImage imageNamed:#"myImage.png"];
This works swimmingly. My app is essentially an image viewer. I wanted to cache next and previous images by preloading a subview with an image. When I place myViewController into a variable like this:
UIViewController *pager = myViewController;
And attempt to use the variable to set the image for the UIImageView like this:
pager.myImageView.image = [UIImage imageNamed:#"myImage.png"];
I get an "error: request for member 'myImageView' in something not a structure or union". I've tried doing it using square brackets:
[[[pager myImageView] setImage:[UIImage imageNamed:#"myImage.png"]];
I get "warning 'UIViewController' may not respond to '-myImageView' ". How do I access the hierarchy to get to myImageView? I've used a #class in the header and a #import and I've synthesized the class instance. The only solution I have so far is a hack, by doing this:
UIImageView *pager = myViewController.myImageView;
pager.image = [UIImage imageNamed:#"myImage.png"];
Which doubles the amount of variables I have, one for the UIImageView variable, and one for the UIViewController variable. Thanks in advance to anyone that can help out.
quick answer:
You'll need to change the line:
UIViewController *pager = myViewController;
to:
MyViewController *pager = myViewController;
To be able to access the custom properties you added in your MyViewController class.
explanation:
Your MyViewController class probably looks something like this (simplified):
// MyViewController.h:
#interface MyViewController : UIViewController
{
UIImageView * myImageView;
}
#property (readonly) UIImageView * myImageView;
#end
// MyViewController.m:
#implementation MyViewController
#synthesize myImageView;
#end
You have extended the UIViewController class by adding a member variable and a property (myImageView) to the original class.
When you use a pointer to the base class (UIViewController) to access your variable, you are telling the compiler to treat that variable as if it was a plain old UIViewController, and a plain old UIViewController has no concept of a myImageView. This is what causes the compiler errors.