Getting WebView AbsoluteString In Second View - ios

I'm in need of some detail. I've looked through multiple books/forums, and I've tried as best as I can on my own. However, at this point I'm in need of some outside help with simple directions, if at all possible. I'm trying to make this as easy as possible to get the goal across, and if you need anything, just ask.
So, here is what I'm trying to do:
What I have:
I currently have the WebView view, and the bookmarks view with a table in it. I have NSUserDefaults passing the current URL to the bookmarks view and displaying it (but not saving it)
What the goal is:
I need to add an "Add Bookmarks" button underneath the rest of the table that gets the current string of the WebView in the other view and saves it as an entry to the table.
What I need:
So, at this point (as a start) I need a method of getting the URL of the WebView from the other view and transferring it to the bookmarks view to be saved as a row to the table.
I was thinking of doing this by calling absoluteString and saving to user defaults, but I need assistance for the code portion. I don't know the full usage of NSUserDefaults yet, but I do have some experience with it.
Thanks for the help! It's greatly appreciated!
Jake

You should pass the current URL and Title to the Bookmarks view before you open it:
[bookmarks setUrl:webView.request.URL.absoluteString];
[bookmarks setTitle:[webView stringByEvaluatingJavaScriptFromString:#"document.title"]];
... open bookmark view now ...
A full example would be:
- (IBAction)bookmarksPressed:(id)sender {
// Open the book marks controller passing it the current URL and Title
BookmarksViewController * bookmarksViewController = [[[BookmarksViewController alloc] initWithNibName:#"BookmarksViewController" bundle:nil] autorelease];
[bookmarksViewController setUrl:webView.request.URL.absoluteString];
[bookmarksViewController setTitle:[webView stringByEvaluatingJavaScriptFromString:#"document.title"]];
[self presentModalViewController: bookmarksViewController animated:YES];
}

Related

Manually pushed view controller freezes app

I am trying to manually push a view controller within my iOS 8 app. I have designed it in the Main.storyboard and i have already attached on it an specific identifier.
The code i am using is:
CustomViewController *vc =
[self.storyboard instantiateViewControllerWithIdentifier:#"CustomViewController"];
vc.customField1 = self.customField1;
vc.customField2 = self.customField2;
[self.navigationController pushViewController:vc animated:YES];
but that causes the app's freeze. It does not spit out any logs or something, so I cannot understand what might be wrong.
Can you help me a bit here?
Thank in advance
Do not do these two lines:
vc.customField1 = self.customField1;
vc.customField2 = self.customField2;
The problem here is that you're assigning one text field to be another text field (actually, you're making a text field reference refer to a completely different text field). Instead, copy the contents (e.g. the text) of the fields from your parent view controller to fields that already live in your new CustomViewController:
vc.customField1.text = self.customField1.text;
vc.customField2.text = self.customField2.text;
I'm thinking what is happening here is that the app is hanging when the new CustomViewController appears because it's trying to access fields in the now hidden / pushed-away parent view controller.

Keep WebView on Current Page

I have a view controller that has a web view. What I'm wanting to know is if it is possible to keep the current page info available so that if the user goes elsewhere in my app and comes back to the web view, it will show the last page the user was on and not always load the url defined in viewDidLoad.
Save your current url in view will disappear to lets say NSUserDefaults like:
-(void)viewWillDisappear:(BOOL)animated{
NSString *currentURL = currentWebView.request.URL.absoluteString;
//save to NSUserDefaults
}
then in viewWillAppear get this url and load webview

Pass data between UITabBarController views

I have searched for an entire day for a simple example on this and haven't found it yet. I am working on an app and I want to make an API call on the initial load and populate some variables that will be accessible in any of the tab views.
Example: I want to make a single API call to get my data which will include data relating to alerts for the alerts tab. I want to use this data on the initial load to update the "Alerts" tab badge. However, I want to also use that information on the alerts page once the person goes to that tab without having to make another API call to get the same information again.
I have read a ton of explanations that do not fit my requirements, so I am looking for a good example to help me out.
Use your UITabBarViewController's viewControllers property to access the array of view controllers in your tab bar controller. Typecast them according to your architecture of tab bar controller.
Then get a pointer to any of view controller using that array.
For example, say your tab bar controller has 5 tabs, each tab having a UINavigationController which has particular UIViewController as root view controllers. Say you want to set badge value of 3rd tab to your web response array count. You can do that as
[[[self.tabviewController viewControllers] objectAtIndex:2]
setBadgeValue:[NSString stringWithFormat:#"%d",[myArray count]];
You can also get to particular view controller's property by typecasting the view controllers. For example
MyViewController *myVc = (MyViewController*) [[(UINavigationController*)[[self.tabviewController viewControllers] objectAtIndex:2] viewControllers] objectAtIndex:0];
[myVc setPropertyName:propertyValue];
I had this question typed up since yesterday and made sure to search before posting. There was no question similar that I found that had the answer, and it may be very straight forward or maybe this is not the way to do it but here is how I solved this issue: using NSUserDefaults and the code example on this page
Put the data in your app delegate object. You can access it from anywhere in your app by (MyAppDelegate *)[[UIApplication sharedApplication] delegate], or you can give each of your view controllers an explicit link to it.
NSUserDefaults isn't really meant for sharing data globally in your app, although it would get the job done. It also has the benefit that the information sticks around if your app can't connect to the server next time. Is that a good thing or a bad thing?
Put all of those variables in a single class and access a shared instance of it whenever you want.
Add
+ (YourClass *)sharedObject
{
static YourClass *sharedClassObject = nil;
if (sharedClassObject == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClassObject = [[YourClass alloc] init];
//Initialise it here if necessary
});
}
return sharedClassObject;
}
To access the shared instance, simply use [YourClass sharedObject].
You should use NSNotificationCenter to post the notification that new data arrived and your new data as an object.
Each of the viewControllers that need that object should just subscribe to that notification and just consume the new data.

Saving iOS Application Scene State

This may be impossible, but I'm trying to save the state of my application between scene transitions, but I can't figure out what to do. Currently I love the way that when you have an application running and hit the home button, you can go back to that application just where you left off, but if you transition between scenes (in a storyboard), once you get back to that scene the application state was not saved.
I only have two different scenes that need to be saved (you transition back and forth from one to the other). How can I go about saving a storyboard scenes state without taking up precise memory?
More Detailed: Here is my entire storyboard. You transition back and forth between scenes using the plus toolbar button. On the second scene the user can tap on the table view cells and a real image will fill the image view (See figure 1.2)
Figure 1.1
In figure 1.2 you see what happens when you tap inside one of the many table view cells (an image view pops up.)
Figure 1.2
THE PROBLEM: When you tap a table view cell, which fills an image view (shown in figure 1.2) it works fine if you stay on that scene or even hit the iPhone home button (if you hit the iPhone home button and then reopen the app the scene's state was saved and the image view filled with a simple image still shows just like we left it), but if I transition (using the plus button) back to the first scene, and then use the plus button on the first scene to get back to the second scene the image view that I created (shown in figure 1.2) disappears and the second scene loads without saving the state and image views we filled.
EDIT: I tried using the same view controller for both scenes, but it didn't solve the problem.
UPDATE: I just found the following code (that I think stores a views state). How could I use this and is this what I've been looking for?
MyViewController *myViewController=[MyViewController alloc] initWithNibName:#"myView" bundle:nil];
[[self navigationController] pushViewController:myViewController animated:YES];
[myViewController release];
I would suggest a combination of two things:
1. Take DBD's advice and make sure that you don't continuously create new views
2. Create a shared class that is the data controller (for the golfers, so that the data is independent of the scene)
The correct way to make the segues would be to have one leading from the view controller on the left to the one on the right. However, to dismiss the one on the right you can use
-(IBAction)buttonPushed:(id)sender
[self dismissModalViewControllerAnimated:YES];
}
This will take you back the the view controller on the left, with the view controller on the left in its original state. The problem now is how to save the data on the right.
To do this, you can create a singleton class. Singleton classes have only one instance, so no matter how many times you go to the view controller on the right, the data will always be the same.
Singleton Class Implementation (Of a class called DataManager) - Header
#interface DataManager : NSObject {
}
+(id)initializeData;
-(id)init;
#end
Singleton Class Implementation (Of a class called DataManager) - Main
static DataManager *sharedDataManager = nil;
#implementation DataManager
+(id)initializeData {
#synchronized(self) {
if (sharedDataManager == nil)
sharedDataManager = [[self alloc] init];
}
return sharedDataManager;
}
-(id)init {
if(self == [super init]) {
}
return self;
}
#end
Then, inside your view controller code you can grab this instance like this
DataManager *sharedDataManager = [DataManager initializeDataManager];
This way you will have the same data no matter how many times you switch views.
Also, you can better adhere to MVC programming by keeping you data and your view controllers separate. (http://en.wikipedia.org/wiki/Model–view–controller)
Figure 1.1 has a fundamental flaw which I believe the basis of your problem.
Segues (the arrows between controllers on the storyboard) create new versions of the UIViewControllers. You have circular segues. So when you go "back" to the original screen through the segue is really taking you forward by creating a new version.
This can create a major problem for memory usage, but it also means you can't maintain state because each newly created item is an empty slate.
Since your are using a UINavigationController and pushViewController:animated: you should "pop" your controller to get rid of it.
On your "second" scene, remove the segue from the + button and create an IBAction on a touchUpInside event. In the IBAction code add the "pop"
- (IBAction)plusButtonTapped {
[self.navigationController popViewControllerAnimated:YES];
}
I see what you mean. This should happen to every application, as when the last view controller in the navigation stack is transitioned away from, it is deallocated and freed. If you need to save values such as text or object positions, a plist may be the way to go. See this related question for how to use a plist.
Apple isn't going to do this for you. You should probably just save the state of each view using NSUserDefaults and each time your application launches re-load your saved data.
If you are storing everything in CoreData you would only need to save the active view and a few object ids, if not you would need to save any data you have.
Don't expect iOS to save anything that you have in memory between launches. Just store it in NSUserDefaults and load it each time.
Store the state of the scene in NSUserDefaults or inside a plist file then when loading up the scene just load it with the settings from there. If the images are loaded from the internet you might also want to save them locally on your iphones hard drive so it runs a bit smoother.
I don't think you should cycle the segues, just use one that connects viewcontroller 1 from viewcontroller 2 should be enough and that way you make sure that no additional viewcontrollers are being made (memory problems maybe?)
However for your particular problem, I believe that you should use core data to save the exact state of your table, view because ios doesn't save the exact state of view at all times. it will require work but you will achieve what you want. You will need to save the exact photo( using a code or enums that will be saved), the location in the table view, the score or well whatever data you need to save that state.
The best of all is that coredata is so efficient that reloading the data when the app is relaucnhed or into foreground it takes no time, and ive used core data to load more than 5k of records until now and works just fine and its not slow at all.
When i get back home ill provide a code you might use to get an idea of what i mean.
The key here is to:
Have some sort of storage for the data that your application needs. This is your application's data model.
Give each view controller access to the model, or at least to the part of the model that it needs to do its job. The view controller can then use the data from the model to configure itself when it's created, or when the view is about to appear.
Have each view controller update the model at appropriate times, such as when the view is about to disappear, or even every time the user makes a change.
There are a lot of ways that you can organize your data in memory, and there are a lot of ways that you can store it on disk (that is, in long term storage). Property lists, Core Data, plain old data files, and keyed archives are all possibilities for writing the data to a file. NSArray, NSDictionary, NSSet, and so on are all classes that you can use to help you organize your data in memory. None of that has anything to do with making your view controllers feel persistent, though. You'll use them, sure, but which one you choose really doesn't matter as far as updating your view controllers goes. The important thing, again, is that you have some sort of model, and that your view controllers have access to it.
Typically, the app delegate sets up the model and then passes it along to the view controllers as necessary.
Something else that may help is that you don't have to let your view controller(s) be deleted when they're popped off the navigation stack. You can set up both view controllers in your app delegate, if you want, so that they stick around. You can then use the ones you've got instead of creating new ones all the time, and in so doing you'll automatically get some degree of persistence.

iOS Using HTML links to fetch data

I'm creating an interactive book for learning languages. It has reading, quiz and some simple game. The content of each chapter is an HTML file. The book allows the user to learn about 300 words that exist in the text. Earch word was enclosed in a link like this: < a href="word">word< /a> when the user touch the link, a modal view appears with the translation and information about that word.
This is the code I'm using to get the link:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// currentWord is the word in the link
self.currentWord = [[request URL] lastPathComponent];
// okToCatchLinks is a BOOL that I use to avoid showing the modalview when the page
// is loaded for the first time.
if (okToCatchLinks){
NSLog(#"Ok to catch links!");
WordViewController *viewController = [[WordViewController alloc] initWithNibName:#"WordViewController" bundle:nil]
// I did my homework creating the delegate protocol to dismiss the modal view.
viewController.delegate = self;
// This is a label in the modalview showing the word in the HTML link.
viewController.labelTitle = self.currentWord;
// Create a Navigation controller to add the "DONE" dismiss button
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:viewController];
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
// show the navigation controller modally
[self presentModalViewController:navController animated:YES];
// Clean up resources
[navController release];
[viewController release];
}
okToCatchLinks = YES;
return YES;
}
With this code I will get the selected word in a string variable. Then I search that word in the DB using CoreData.
The HTML is UTF-8 encoded. I have to be able to search for that word in different languages. So if the user click on the word (日本語) or (résumé) I have to be able to find it in the DB.
For the data store I converted an CSV file to a .sqlite file.
I wonder if there is a better way to do this. Personally I don't like to use < a href=""> to get the link, but I couldn't find any other way to get the current word in a link. Also performing the search of that word in the DB is not clean, because I'm not using any UID for each row. I just compare the string holding the word with the words in the corresponding column of the DB.
Is there a more efficient way to do this?
To resume: Having a list of words, mark those words in the HTML so the user can touch and retrieve information about that word from a DB.
Some observations:
UIDs: As long as your words are unique, you can use that column as the primary key without using a separate UID. Actually, Core Data will create one for you anyway, so you can use objectWithID to retrieve your word.
HTML Links: Your solution with UIWebView, HTML files and links seems feasible to me. However, you could of course also use a UITextView and handle the selections programmatically, working with NSRange. That would perhaps be more complicated, but also more "native".
Delegate: You only need a delegate if you want to pass information back to the view that first presented the model view controller (too much homework! ;-)). Just dismiss it from within with
[self dismissModalViewControllerAnimated:YES];

Resources