Opening a new UIViewController when a link on a UIWebView is clicked - objective-c

I found this thread which matches my problem: Clicking a link in UIWebView pushes onto the NavigationView stack
However, the following situations are different:
Instead of using a navigationController, I am using a View Based application to manually switch to different viewcontroller when corresponding buttons are pressed.
Rather than using links I am using onClick method to detect when a user clicks on something on a UIWebView and trying to open a new ViewController with a new UIWebView.
Will the same code work or do i have to make some changes?

You will have to do some modifications
1st:
when onClick, you will have to redirect your current page to a new location
This location for example will be MYLocation://GoThere
2nd:
Update the shouldStartLoadWithRequest
-(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
NSString *urlString = url.absoluteString;
//Check if special link
if ( [ urlString isEqualToString: #"MYLocation://GoThere" ] ) {
//Here present the new view controller
MyViewController *controller = [[MyViewController alloc] init];
[self presentViewController:controller animated:YES];
return NO;
}
return YES;
}

I think you may use the shouldStartLoadWithRequest as Omar posted.
If the new viewcontroller has a webviewcontroller, then you should pass the url (or even the request) to the new viewcontroller to show the clicked URL.

Related

Custom BackButton in iOS webview

I want to create custom back button on each page in my iOS web-view (objective-C). Can anyone please suggest me how to implement.
Thanks in Advance
I am new to iOS Web-view that is why I posted directly what I want this is my code :
#import "ViewController.h"
#interface ViewController () <UIWebViewDelegate>
#end
#implementation ViewController{
__weak IBOutlet UIImageView *logoImage;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.webView.delegate = self;
NSString *urlString = #"https://www.anything.com";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:requestObj];
self.webView.hidden = YES;
if ([_webView canGoBack]) {
[_webView goBack];
}}
- (void)webViewDidStartLoad:(UIWebView *)webView {
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.webView.hidden = NO;
logoImage.hidden = YES;
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"%#", error);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I am passing a url of my website and I want to show a back button on every page of my application so that user can go to previous page. I have one more issue that whenever I minimise my application and opens it back from recent apps it get restarted while I want it to be resumed from the same screen
For the UIWebView, check the method - (void)goBack; out.
For the WKWebView, check the method - (WKNavigation *)goBack; and - (WKNavigation *)goBack:(id)sender; out.
UPDATE
// init the back button
[btnBack addTarget:self action:#selector(backButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
- (void)backButtonTapped:(id)sender
{
[self.webview goBack];
}
Assume you have set up your webview and back button (in the navigation bar or somewhere else) in the full code.
The browser could go back when the current (tab) page has a history list and the current page url is not the first and last one.
Make sure you really want to goBack in viewDidLoad, it needs some time to finishing loading the target page you specified (https://www.anything.com) just like you do it in a browser.
The method loadRequest of webview is asynchronous, it pushes the url request to webview and returns it immediately.
Bind the webview's goBack method to your custom back button like the above code snippets.
Try to click a sample link in the initial page (https://www.anything.com), once the new page finishes loading, it could be able to goBack with satisfying [self.webview canGoBack].
Make a breakpoint in the implementation of method backButtonTapped, if needed.
Drop the logoImage or other unrelated code issues in your next question, that makes your question more clear and helpful.

Using NSNotification to detect UIButton push to change UIWebView

I have two buttons in my VC and currently they both are connected to their respective pdf files. There is another button, Push, that goes to the rootviewcontroller. In the RVC I have a UIWebView. How can I make it so that if I push button A, a.pdf is displayed and b.pdf for button B?
I guess, how do I make the rvc listen for this event correctly?
Snippet for UIWebView inside my RVC.m
pdfViewer=[[UIWebView alloc]initWithFrame:CGRectMake(420, 190, 340, 445)];
NSString *path = [[NSBundle mainBundle] pathForResource:#"nameofpdf" ofType:#"pdf"];
NSURL *targetURL = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[pdfViewer setAlpha: .8];
[pdfViewer loadRequest:request];
[self.view addSubview:pdfViewer];
And button Push in my VC
-(IBAction)pushButtonPressed{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
RootViewController *RVC = [storyboard instantiateViewControllerWithIdentifier:#"Root"];
[UIApplication sharedApplication].delegate.window.RootViewController = RVC;
}
Wrap up:
I first press A or B, then press Push which will bring me to the RVC which will display pdfA or pdfB.
Declare a property for your RVC for the pdf file you want it to open. In your viewController that holds the buttons have the buttonFunction pass the filename to the segue as the sender parameter in prepareForSegue. In prepareForSegue grab the dest view controller and set the property. A short snippet might be more clear
In your viewController with the Buttons:
- (void)buttonA {
[self performSegueWithIdentifier:#"mySegue" sender:#"pdfA.pdf"];
}
- (void)buttonB {
[self performSegueWithIdentifier:#"mySegue" sender:#"pdfB.pdf"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"mySegue"]) {
UIViewController *rvc = segue.destinationViewController;
rvc.pdfToOpen = sender;
}
}
You'll have to adjust your prepareForSegue to use the correct class with the correct property name of course, but this is generally how you should pass information from an action into a view controller via a segue.
Once in your RVC you can access that property, probably inside viewDidLoad, and tell your webView to load the correct pdf.

Generic "help" view which is accessible from all views in uinavigationView

Assuming an application has many views pushed to a uinavigationViewController, each view is different in content.
since the elements in the app are complex, I would like to show a small help view for specific views or elements in a specific view.
Imagine a "?" button that when pressed on will pop a new view in the center of the screen, playing youtube help video, or just a textual HTML loaded from a remote server.
Question: what is the best strategy for doing such a thing?
where should I place this code (App Delegate?)
if so, how would I call it from other views (with URL Parameter)
-(void)showHelpView:(NSString *)theURLString{
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)];
//webView.delegate= self;
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:theURLString]]];
[window addSubview:webView];
[window makeKeyAndVisible];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
}
I'd prefer using pseudo-modal views. So that you create view every time, and add it on top of current window. I've used this approach for OAuth 2.0 authorisation popup in my last app.
Basically you create custom UIViewController subclass, provide custom initialisator, such as, for example:
// - (id) initWithURL: (URL*)url {
// ...
// self.url = url;
// ...
// return self;
PseudoModalViewController* pmvc = [[PseudoModalViewController alloc] initWithURL:#"http://www.google.com/"];
After you've created view you add it on top of current window:
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
if(!window)
{
window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
}
[window addSubview:pmvc.view];
In, for example, viewDidLoad you load url obtained in initialisator:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
}
You may even design UI in Interface Builer for that. As a last piece I'd recommend add to include full screen transparent background view to ensure that all views below are disabled.
Oh, almost forgot, you hide that view with just a simple [self.view removeFromSuperview]. Don't forget to provide correct memory management for that view (i.e. release it in time, etc).

Best design pattern for a window opening another window in cocoa application

I am learning how to create osx applications with Cocoa/Objective-C. I am writing a simple app which will link together two different tutorials I have been going through. On start up a choice window loads with 2 buttons, one button loads one window and the other loads the other window. When either button is clicked the choice window closes.
The choice window controller object was added to the MainMenu.xib file so it is created at launch. The window is then opened using the awakeFromNib message.
I want the result of one button to open up the 'track controller' tutorial application from the ADC website. The action looks like this:
- (IBAction)trackButton:(id)sender {
TMTrackController *trackController = [[TMTrackController alloc] init];
[self.window close];
}
I added an init method to the TMTrackController class which looks like this:
- (id) init {
if (self = [super init]) {
[self showWindow];
TMTrack *myTrack = [[TMTrack alloc] init];
myTrack.volume = 50;
self.track = myTrack;
[self updateUserInterface];
return self;
}
else {
return nil;
}
}
- (void) showWindow {
if(!self.window) {
[NSBundle loadNibNamed:#"trackWindow" owner:self];
}
[self.window makeKeyAndOrderFront:self];
}
I am not sure this is the best way to be doing this as I know that the choiceController class will be released when it is closed thus getting rid of the TMTrackController class too. However even when I untick the 'release when closed' box of the ChoiceWindow.xib it breaks too.
What is the correct way to do this?
With xib s in the same project use:
#interface
#property (strong) NSWindowController *test;
#implementation
#synthesize test;
test = [[NSWindowController alloc] initWithWindowNibName:#"XIB NAME HERE"];
[test showWindow:self];
[home close];
It is not completely the same but this is my solution for such problems: Stackoverflow
Just ignore my statement in this answer regarding showing the window as a modal window. Everything else is still valid. This way you could have your personal window controller and it controls everything there is within the xib. This is a huge advantage for maintaining the project afterwards (and you keep to the application logic).

How to update DetailView using MasterDetail Application Template

I'm new to using the split view for creating iPad applications. When I first create the project just using the standard MasterDetail Application template (Xcode 4.2), it creates a MasterViewController and a DetailViewController. The template has the following method that is called when a row is selected from the popover table (master detail view controller):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
if (!self.detailViewController)
{
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
[self.navigationController pushViewController:self.detailViewController animated:YES];
Now I understand when you are using a regular navigation controller if you are programing for an iPhone you just do this type of thing to push on another view controller on to the stack. However, with this template, it just pushes the detail view onto the popover rather than updating what is already present. I'm confused as what I need to update to select something from the pop over (master detail view), and then have the detailView update.
Update:
To try and test out the "detailItem" that is already setup for you in the DetailViewController, I commented out the pushViewController and added the following:
//[self.navigationController pushViewController:self.detailViewController animated:YES];
self.detailViewController.detailItem = #"Test";
// setter in detailViewController
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
// detailDescriptionLabel.text is a IBOutlet to the label on the detailView
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
According to this code, the text of the label on the detailViewController should be updated. However, when I do click on the item in the master view controller table, nothing happens.
There are a couple different ways you could do it. First off, like you said, remove the pushViewController call (I don't know why Apple's template does this... maybe just to show you you can?).
Next, let your MasterViewController know about the DetailViewController that is already displayed. I usually set master.detailViewController = detailViewController in the appDelegate.
Remember, the DetailViewController is already being displayed, so you won't always need to realloc it (unless you are replacing it with some other view)
First Option
Use delegate calls to set the information. Declare a protocol to pass information to the detailView and have it display it appropriately. Here is a tutorial describing this in more detail.
Second Option
Pass DetailViewController some data & override the setter to refresh the detailView. Here is a tutorial describing this in more detail.
// in DetailViewController
- (void)setDetailItem:(id)newDetailItem {
if (detailItem != newDetailItem) {
[detailItem release];
detailItem = [newDetailItem retain];
// Update the view.
navigationBar.topItem.title = detailItem;
NSString * imageName = [NSString stringWithFormat:#"%#.png",detailItem];
[self.fruitImageView setImage:[UIImage imageNamed:imageName]];
}
}
Edit: Just looked at the template again, and setDetailItem type code is already in there, but the code is creating a completely new detailView so the detailView that is viewable on the splitViewController is not changed at all.