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

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).

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.

UIPageViewControllerSpineLocation Delegate Method Not Firing

Major head-scratcher all day on this one :-(
I have an instance of a UIPageViewController that does not appear to be firing the delegate method:
-(UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
I have tried various methods of displaying the UIPageViewController and have settled on a programatic approach (as opposed to a Storyboard one) that appears to be working correctly, with one exception... when rotating the iPad to landscape the spine does not appear mid-point as expected. I simply cannot find out why the delegate method does not get called.
Code Explanation (simplified for example)
Consider three classes as follows:
RootViewController - loaded when the app starts
PageViewController - loaded by RootViewController upon user initiation
PageContentViewController - loaded by PageViewController when pages are needed
Fairly self-explanatory. The RootViewController is loaded by the app upon launch. When the user taps an image within this view controller's view (think magazine cover opening a magazine) it launches the PageViewController as follows:
PageViewController *pvc = [[PageViewController alloc] initWithNibName:#"PageView"
bundle:[NSBundle mainBundle]];
pvc.view.frame = self.view.bounds;
[self.view addSubview:pvc.view];
In the actual app there is animation etc to make the transition all nice, but essentially the PageViewController's view is loaded and takes fullscreen.
PageViewController
This is the workhorse (only relevant methods shown). I have tried various examples from the infinite world of Google and written directly from the Apple docs...
#interface PageViewController : UIViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
#property (nonatomic, strong) UIPageViewController *pageViewController;
#property (nonatomic, strong) NSMutableArray *modelArray;
#end
#implementation TXCategoryController
-(void)viewDidLoad
{
[super viewDidLoad];
// Simple model for demo
self.modelArray = [NSMutableArray alloc] init];
for (int i=1; i<=20; i++)
[self.modelArray addObject:[NSString stringWithFormat:#"Page: %d", i]];
self.pageViewController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.delegate = self;
self.pageViewController.dataSource = self;
PageContentViewController *startupVC = [[PageContentViewController alloc] initWithNibName:#"PageContent" bundle:nil];
startupVC.pageLabel = [self.modelArray objectAtIndex:0];
[self.pageViewController setViewControllers:[NSArray arrayWithObject:startupVC]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
self.pageViewController.view.frame = self.view.bounds;
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
// Relevant code to add another view...
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
// Relevant code to add another view...
}
-(UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
// Setting a break point in here - never gets called
if (UIInterfaceOrientationIsPortrait(orientation))
{
// Relevant code to create view...
return UIPageViewControllerSpineLocationMin;
}
// Relevant code to create 2 views for side-by-side display and
// set those views using self.pageViewController setViewControllers:
return UIPageViewControllerSpineLocationMid
}
#end
This all works perfectly well as I mentioned earlier. The PageViewController's view gets shown. I can swipe pages left and right in both portrait and landscape and the respective page number appears. However, I don't ever see two pages side-by-side in landscape view. Setting a breakpoint in the spineLocationForInterfaceOrientation delegate method never gets called.
This is such a head-scratcher I have burned out of ideas on how to debug/solve the problem. It almost behaves like the UIPageViewController isn't responding to the orientation changes of the device and therefore isn't firing off the delegate method. However, the view gets resized correctly (but that could be just the UIView autoresizing masks handling that change).
If I create a brand new project with just this code (and appropriate XIb's etc) it works perfectly fine. So something somewhere in my actual project is causing this. I have no idea where to continue looking.
As usual, any and all help would be very much appreciated.
Side Note
I wanted to add the tag 'uipageviewcontrollerspinelocation' but couldn't because it was too long and I didn't have enough reputation (1500 required). I think this is a devious ploy on Apple's part to avoid certain tags in Stackoverflow... ;-)
Finally found the problem. It was something of a red herring in its symptoms, but related just the same.
Putting a break point in the shouldAutorotateToInterfaceOrientation: method was a natural test to see if the UIViewController was even getting a rotation notification. It wasn't which led me to Apple's technical Q&A on the issue: http://developer.apple.com/library/ios/#qa/qa1688/_index.html
The most relevant point in there was:
The view controller's UIView property is embedded inside UIWindow but alongside an additional view controller.
Unfortunately, Apple, in its traditional documentation style, doesn't provide an answer, merely confirmation of the problem. But an answer on Stack Overflow yielded the next clue:
Animate change of view controllers without using navigation controller stack, subviews or modal controllers?
Although my RootViewController was loading the PageViewController, I was doing it as a subview to the main view. This meant I had two UIViewController's in which only the parent would respond to changes.
The solution to get the PageViewController to listen to the orientation changes (thus triggering the associated spine delegate method) was to remove addSubview: and instead present the view controller from RootViewController:
[self presentViewController:pac animated:YES completion:NULL];
Once that was done, the orientation changes were being picked up and the PageViewController was firing the delegate method for spine position. Only one minor detail to consider. If the view was launched in landscape, the view was still displaying portrait until rotated to portrait and back to landscape.
That was easily tweaked by editing viewDidLoad as follows:
PageContentViewController *page1 = [[PageContentViewController alloc] initWithNibName:#"PageContent" bundle:nil];
NSDictionary *pageViewOptions = nil;
NSMutableArray *pagesArray = [NSMutableArray array];
if (IS_IPAD && UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
{
pageViewOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:UIPageViewControllerSpineLocationMid]
forKey:UIPageViewControllerOptionSpineLocationKey];
PageContentViewController *page2 = [[PageContentViewController alloc] initWithNibName:#"PageContent" bundle:nil];
[pagesArray addObject:page1];
[pagesArray addObject:page2];
}
else
{
[pagesArray addObject:page1];
}
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:pageViewOptions];
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:pagesArray
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
Job done and problem solved.

load data into a UIWebView created programmatically

I'm setting up a ViewController that will hold a couple of UIViews, including UIWebView
I'm creating and adding those Views programmatically in the viewDidLoad method.
Is this approach satisfactory? or am I doing something too expensive for the hardware.
and how do I load new data into a WebView that was created in the ViewDidLoad method.
I have a properly of type UIWebView, then in the ViewDidLoad i do:
detailsView = [[UIWebView alloc] initWithFrame:CGRectMake(500, 0, 512, 768)];
and this does not seem to work:
- (void)loadDetailedContent:(NSString *)s
{
[detailsView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
[detailsView reload];
}
Waiting for your reply, thank you in advance
You can certainly create and add subviews in your viewController's viewDidLoad programmatically, but you'll have to remember to call addSubview if you want them to appear on the screen.
- (void)viewDidLoad
{
[super viewDidLoad];
detailsView = [[UIWebView alloc] initWithFrame:CGRectMake(500, 0, 512, 768)];
[self.view addSubview:detailsView];
}
As for your second question about loading data into your webview, there is no need to call reload immediately after calling loadRequest:.
- (void)loadDetailedContent:(NSString *)s
{
[detailsView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
}

How can i make my application allways start with the second view?

I have a Web Service application and it has alogin view. I want to make my application's login view come when the first time app is loaded(installed) and after that it must allways start with a second view. How can i make it? In this link there are some solutions but i think this isn't what i'm looking for. Since mine is a web servise, mean the content of the second view(which i want to be pushed allways) is fetched from a server(i use NSJSONSerialization class for this work)
I would do the login view as a modal view which is only presented when needed.
Edit:
This is VERY brief: (I assume that you are using ARC.)
In AppDelegate:
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: mySecondViewController];
if (![self isUserLoggedIn]) {
MyLogInViewController *logInViewController = [[MyLogInViewController alloc] init];
[self presentModalViewController: MyLogInViewController animated: YES];
}
[[self window] setRootViewController: [self navigationController]];
and in logInViewController:
- (void)logInSuccessful {
[self dismissModalViewControllerAnimated: YES];
}

Cancel button/touch for UIWebView

I wonderd if there is any way to make the user dismiss the WebView window if he doesn't want it anymore on the screen...?
I looked at this post but i didn't understand it well.
How to cancel a UIWebView?
can anyone give me an example please?
This is the code i have:
CGSize webScreen1;
webScreen1 = [[UIScreen mainScreen] applicationFrame].size;
CGRect webFrame1 = CGRectMake((webScreen1.width/11.0) ,(webScreen1.height/19.0) ,webScreen1.width/1.2,webScreen1.height/1.25);
defaultWebView.frame = webFrame1;
self.defaultWebView = [[UIWebView alloc] initWithFrame:webFrame1];
self.defaultWebView.backgroundColor = [UIColor whiteColor];
self.defaultWebView.scalesPageToFit = YES;
self.defaultWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
defaultWebView.inputView.hidden = YES;
[self.view addSubview: self.defaultWebView];
[self.defaultWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:
[NSString stringWithFormat:(NSString *)#"%#", #"http://www.google.com"]]]];
Thanks!
There is no such thing as cancel a webView, what you need to do is remove the UIWebView from the parent view. If your UIWebView is a subview of self.view then you can provide a button named Close which behaves like this -
- (IBAction)closeWebView:(id)sender
{
[self.webView removeFromSuperView];
self.webView = nil;
return;
}
This should remove the webview from your view.
...way to make the user dismiss the WebView window...
[self.defaultWebView removeFromSuperview];
look I will explain what happenes in the post you attached above and you will understand the technique,
the UIWebview is a component which views an web page, it may be html or other types,
there is one way to hide your webview is to add an action in the html of the webview and ovveride the request in the code of your app,
when you click on link or ahref or any action on UIwebview there is a delegate method which automatically runs before continuing with the request
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
you can add a button or link on the html page and ovveride the request on this method
for example
Thing to click
and in the delegate method
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ([[[request URL] absoluteString] isEqualToString:#"http://hideWebView/"]) {
[self.webview setHidden:YES];
return NO;
}
return YES;
}
here you ovveride the request http://hideWebView/ and in the delegate you searched for this request and then hiddes the web view or anything else you want to do