I've looked many, many places and have yet to find some good sample code showing how to pre-load the "next" page in a UIPageViewController. There are a few answers on SO detailing some theoretical ways to do it (see this question) but no one has yet to post a working example.
In the workflow of my app I'm showing 1 page per screen and I want to have the "next" screen preloaded because as it is, swiping to the next page can be very slow, sometimes requiring 2 swipes (if you swipe too fast) in order for the next page to be rendered and shown. This provides a bad user experience. I don't really care about preloading the "previous" or any other screens as the typical workflow will be that users stay on a screen for a while before moving to the next screen (to the right). I'm using the slide animation (not curl up). I create all views programatically and do not use IB at all.
I've tried to store some UIViewControllers in an NSMutableArray and load the controllers from there, but it's tricky to get working right and didn't seem to speed anything up. There must be a good way to do this.
Any help is greatly appreciated.
I've solved for my case in a somewhat of hack. For each ContentView, I have a UIImageView within a UIScrollView for zooming. My problem was that upon booting up the app, if the user zoomed before swiping, going to the next page while zoomed in wouldn't work too well. I use the following code (Swift 1.2) to solve this problem. As I've said, it's a bit of a hack though.
var layoutsubs = false
override func viewDidLoad() {
super.viewDidLoad()
//Other code for implementing pageViewController omitted
//add pageViewController to main view
self.addChildViewController(pageViewController)
self.view.addSubview(pageViewController.view)
pageViewController.didMoveToParentViewController(self)
//Load to the viewController after the starting VC, then go back to the starting VC
var viewControllers = [afterVC]
pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
viewControllers = [startingVC]
pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Reverse, animated: true, completion: nil)
}
override func viewWillLayoutSubviews() {
//Load the viewController before the starting VC then go back to the starting VC
//viewWillLayoutSubviews() is called multiple times, so do this only once
if !layoutsubs {
let startingVC = self.viewControllerAtIndex(imageIndex) as ContentViewController
let beforeVC = pageViewController(pageViewController, viewControllerBeforeViewController: startingVC) as! ContentViewController
var viewControllers = [beforeVC]
pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Reverse, animated: true, completion: nil)
viewControllers = [startingVC]
pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
layoutsubs = true
}
}
Essentially, I load the view controllers before and after the starting view controller. I do this by setting each one to the VC to see via setViewControllers(_:direction:animated:completion:) (see ref), then by going back to the starting view controller. Why is this in two different functions? Well, if you put it all in one, only one of the two view controllers next to the starting VC will load. This may be desirable for some cases, but I needed all three VCs (before, starting, and after) to load.
I'm not sure how well this method would work if the UIPageViewController was already loaded. For instance, if you needed to load the page 2 away from the page being viewed, after a few swipes. It may skip around if you put it in willTransitionToViewControllers().
Related
How to get to $(document) in the onReady method ?
I've tried with smoothState.cache[smoothState.href] but couldn't make it works.
(fantastic plugin)
Thanks
I'm assuming you want to get at the document for the new page that's being loaded. Technically, $(document) exists in onReady, and it's the first document you visited on the site. After that, smoothState just dynamically updates it by swapping out content. So whatever existed on the previous page you were viewing is there as normal. Once the line $container.html( $newContent ); runs in your onReady method (assuming your code follows all the smoothState examples), you new content should be available.
However, if you want to get at the actual, full document for the new page that got loaded up, not just what's contained in your wrapper div that gets swapped out, it's contained in smoothState.cache[smoothState.href].doc. It's got the header, the body, everything.
A little reading of the smoothState source code shows that you can pull it into a useful format this way:
var $newDoc = $("<html></html>").append( $(smoothState.cache[smoothState.href].doc) );
At this point, you can run find queries or whatever you need to go look through things.
I have a table view controller with a cell that contains an UIImageView. I also have a NSMutableArray that contains the url's. I want the url's to download the images and place them in the correct order. The NSMutableArray also contains some empty Strings and the cell that it corresponds too I want to have my placeholder image from my image assets.
How can I get it to work? I have also populated each cell with a title and summary but cannot workout how images work.
UPDATE
The code used for the image download. Note the photoLabels contains the array of images. Some of the photos are in the incorrect place once the first placeholder image occurs (It is one index late). Why is it doing that. Does anyone know why. I have println(photoLabels) and all the 50 strings are correct (with some just being "")
If anyone can help that would be great.
let imageURL: String = photoLabels.objectAtIndex(indexPath.row) as String
println(imageURL)
if imageURL == "" {
cell.imageContainer.image = UIImage(named: "placeholder")
} else {
cell.imageContainer.setImageWithURL(NSURL(string: imageURL))
}
return cell
Thanks
This seemingly innocent question actually entails a rat's nest of interesting details. These include:
Use lazy loading of the image, loading them just-in-time, rather than trying to download them up front;
Download the images asynchronously;
While downloading the images as needed, cache them (using NSCache, not NSMutableArray) so that if you scroll back to see some images recently downloaded that you don't have to download them again;
But, in response to memory pressure, make sure to empty the RAM-based cache (but still avail yourself of the persistent storage cache);
If user scrolls quickly down to the 100th row in the table example, make sure that the images for the visible cells don't get backlogged behind the requests for the previous 99 images (nb: you should test your app in suboptimal conditions, e.g. a poor 2G or 3G cellular environment, which can be simulated with the network link conditioner); and
You might want a placeholder image to show until the asynchronously retrieved image is downloaded (or retrieved from the cache).
The bottom line is that this takes a non-trivial amount of effort to do properly. As a result, I'd encourage you to use an existing solution, for example the UIImageView categories that are available from SDWebImage or AFNetworking. Both of these two frameworks offer a nice category for UIImageView that allows you to request the image to be downloaded asynchronously (it's sd_setImageWithURL in SDWebImage; it's setImageWithURL in AFNetworking).
I am kind of new to Silverlight and I am having some issues. I have been trying to figure this out for a few hours now...
I have a main page that waits for a packet from a server. When that packet arrives it is handled in a module. Depending on the packet data the module handles an action. Some of those actions involve subroutines on the main page. I access them via:
Dim MainPage As MainPage = App.Current.RootVisual
If strPacketData(1) = "0" Then
MainPage.Do_Sign_In(True, strPacketData(2))
Else
MainPage.Do_Sign_In(False, strPacketData(2))
End If
And this works fine.
Assuming the sign in works the main page calls
Me.Content = New Page2
And the page switches to Page2
The problem is that the same code doesn't work when trying to access subs and controls on page2.
If I try:
Dim Page2 As Page2 = App.Current.RootVisual
If strPacketData(1) = "1" Then
Page2.lblCreateError.Opacity = 100
End If
I get Unable to cast object of type 'SLClient.MainPage' to type 'SLClient.Page2'.
What I am getting from this is that App.Current.RootVisual is set to MainPage, but MainPage shouldn't be open...
I have tried setting App.Current.RootVisual to Page2 but that doesn't seem to do anything.
I am hoping that this issue is caused by my ignorance of silverlight and that there is an easy fix for this...
I guess what I need to do is detect which page is currently being displayed and manipulate the controls and functions\subroutines on that page.
Any Input would be greatly appreciated.
OK well I finally figured it out. Or at least I figured out a way to do it.
I used a dictionary to store the addresses of the pages once they were created in memory. I also modified the program so that rootVisual is its own grid instead of a page. Now the grid loads and clears the pages as children.
I don't know... Silverlight is kind of screwy. Whatever, it works now.
After looking through numerous Stack Overflow posts, I was unable to find a solution to my problem. If I am asking a question that has already been answered elsewhere, kindly give me a nod in the right direction.
And, please, don't be too harsh on me if I might be using the wrong terminology, as I am still kind of new to mobile development.
Problem: All I am trying to do is click a link in a local web page that gets pulled up in a UIWebView. Currently, nothing happens when I click a link, aside from a brief flash of the current page.
This is where I'm at:
1) When the application loads, I show a web view which displays a "local" web page (that's part of my xcode project). So far, so good.
2) When the end user clicks an item (geographical regions in my case, represented by a flag), additional content becomes visible, including a couple of links, leading to "external" web apges, such as wikipedia and a map web site.
3) Most of my little web app is in the web page I show when the application has loaded. I use JavaScript, HTML and CSS to provide most of the functionality (since I already have a web site that does exactly what it is supposed to do).
4) However, being able to link to other web sites from my start page is crucial.
(If you think this is poor design, I'll accept that. Suggestions for better approaches are always welcome; HOWEVER, I do need to solve my current problem before I can restructure my code. Please bear with me.)
Here's the code that brings up the initial web page:
// View Controller.h
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIWebView *spfWebView;
#end
// ViewController.m
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize spfWebView;
- (void)viewDidLoad{
[super viewDidLoad];
//Web Site within XCode Project:
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"www/index" ofType:#"html"] isDirectory:NO];
//NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( [data length] > 0 && error == nil ){
[spfWebView loadRequest:request];
}else if (error != nil){
NSLog(#"Error: %#", error);
}
}];
}
- (void)didReceiveMemoryWarning{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
So in my web page, I am using standard, normal a href links to point to wikipedia, for example, like so (I had to use spaces in the href part to avoid SO from interpreting it as a link, since I only wanted to show you the code for the link):
Wikipedia
And, as I've mentioned, the page just sits there when I click or tap the link. The page seems to reload briefly and flashes on the screen, but that's all I see.
Any ideas anyone?
After additional searching and finally finding some really great information here on SO and a couple of external links, I now see how naive my original question was. It turns out there is more to it than meets the eye.
I wanted a quick fix for a problem that requires a bit more work than I realized.
The most important things I have learned are:
(1) A UIWebView is not necessarily the same as a web browser.
(2) In a UIWebView, you can display web content, yes, but you have to address page clicks separately, within XCode.
(3) If you are planing on running your web app on HTML5, CSS3 and JavaScript, inside a UIWebView, you can do a lot, but you might have to learn about making the UIWebView a delegate to itself, opening additional views, etc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The lessons I have learned come primarily from the following sources:
Clicking a link in UIWebView pushes onto the NavigationView stack
iPhone UIWebView - Open new UIWebView Controller from a hyperlink
UIWebView Link Click
Calling methods from links in a UIWebView
Open a link in a UIWebView
Open iOS6 Apple Maps app from a link in a UIWebView
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So right now, I am refactoring my little phone app, but if anyone has some additional insights as far as making UIWebView links work (in an easy and up-to-date manner), please add your comments, and I will try and implement them into this post.
I have a table that when I click on a row, in a side div I display the details. I have created an event on the row (tr) that is fired every time that row is clicked. The problem I have is that the event is being fired multiple times. It is acting as if a new view is being created everytime the event is fired, but a new view is not being created. Here is the code:
GroupView = class App.GroupView extends Backbone.View
template: App.Utils.getTemplate 'group'
selectedId: 0
events:
'click tr': 'selectGroup'
selectGroup: (e) ->
thisEl = $(e.currentTarget)
$(thisEl).closest('tr').siblings().removeClass 'selected'
$(thisEl).closest('tr').addClass 'selected'
#selectedId = $(thisEl).data().id
#getGroupDetails #selectedId
render: ->
$(#$el).html #template
groups: #collection.models
if #selectedId is 0
firstRowEl = $(#$el).find('tr:first')
$(firstRowEl).addClass 'selected'
#selectedId = $(firstRowEl).data().id
#getGroupDetails #selectedId
getGroupDetails: (id, platform) ->
$('<img/>',
src: 'img/ajax_loader_2.gif'
class: 'ajax-loader'
).appendTo '.group-details'
renderGroupDetails id
groupDetails = new GroupDetailsView
el: '.group-details'
model: groupDetailsModel
renderGroupDetails = (id)->
groupDetails.el = '.group-details'
groupDetails.stopListening groupDetailsModel
groupDetails.listenTo groupDetailsModel, 'change', ->
groupDetails.model =
groupDetails: groupDetailsModel.toJSON()
groupDetails.render()
groupDetailsModel.fetch
data:
id: id
processData: true
The issue is that when I use the groupDetails.stopListening groupDetailsModel, the multiple event firing issue is resolved but then NONE of the events in the groupDetails view is being fired.
Any help is appreciated.
Cheers,
Kianosh
So, giving you a specific answer is difficult: the code you've pasted is a bit hard to follow, and seems like it might be missing bits (eg. what's a groupDetailsModel? Does it have any logic that affects it's view?). That's ok though, because I think what you need here is more debugging help than a specific answer.
If I understand you correctly, your problem is that groupDetails.render() is being invoked extra times, and you're not sure why or what is invoking it: is that correct? If so, your trusty debugger can answer this in a matter of seconds.
NOTE: Everything I'm about to say applies to Chrome (with the built-in developer tools up) or Firefox (with the Firebug extension up) for sure. It probably applies to other browsers with their tools too, I just can't say for sure since I don't use them.
Approach #1: Debugger
With your debugger up, add a debugger; line as the first thing inside your GroupDetails.prototype.render method. This will make your browser pause on that line whenever it hits it. While its paused you can check the "call stack" on the right-hand side, and that will show you exactly what bit of your code just called that render. You can also use the debugger to inspect the values of the different variables in your environment to try and understand why your code is calling render.
Approach #2: Console.trace
If you're not a fan of using the debugger, there's another tool that's also perfect for answering this sort of question. Simply add console.trace() as the first line in your render method, then run your code as normal. This time your browser will log (to the "console") a stack trace generated from that point in the code. This stack trace is similar to the call stack that you can see when you use debugger, and thus it will tell you exactly what code invoked the render each time.
Hope that helps.