iOS UIScrollView Lazy Loading - iphone

i was just wondering if someone could explain this code for me so i can actually learn from it. I am trying to make my app have a scroller that scrolls left to right with loads of pictures (from internet) but the thing is, it must have lazy loading. so i did some tutorials and figured out how to do it but i truly don't understand it. So i was hoping some kind soul would explain how to lazy load step by step
This is the code i had learned from the tutorials:
-(void)scrollViewDidScroll:(UIScrollView *)myScrollView {
/**
* calculate the current page that is shown
* you can also use myScrollview.frame.size.height if your image is the exact size of your scrollview
*/
int currentPage = (myScrollView.contentOffset.y / currentImageSize.height);
// display the image and maybe +/-1 for a smoother scrolling
// but be sure to check if the image already exists, you can do this very easily using tags
if ( [myScrollView viewWithTag:(currentPage +1)] ) {
return;
}
else {
// view is missing, create it and set its tag to currentPage+1
}
/**
* using your paging numbers as tag, you can also clean the UIScrollView
* from no longer needed views to get your memory back
* remove all image views except -1 and +1 of the currently drawn page
*/
for ( int i = 0; i < currentPages; i++ ) {
if ( (i < (currentPage-1) || i > (currentPage+1)) && [myScrollView viewWithTag:(i+1)] ) {
[[myScrollView viewWithTag:(i+1)] removeFromSuperview];
}
}
}

About Lazy loading on scrollView, I would greatly advised to use UITableView instead. Apple did a great job with performance on this component.
You can have them horizontal (see this EasyTableView code, it works great) and stop the page mode if you want a continuous scroll (pagingEnabled = NO;) so you'll be able to get the behavior you are looking for.

Lazy loading is basically fetching large pieces of data (lets say images in this example) only when you need them. In your code, you have a delegate method that is called when you scroll a UIScrollView.
The -(void)scrollViewDidScroll:(UIScrollView *)myScrollView function decides when to actually get data. So as your scrolling, you find out where you are in the scroll view (say you have 10 images you want to load-- you want to know if the screen is currently showing image number 1, 2, 3, etc.). This is what the currentPage integer holds.
Now that you know which page you're looking at, we want to actually fetch the image.
if ( [myScrollView viewWithTag:(currentPage +1)] ) {
return;
}
The code above checks if the image AFTER the image the person is currently looking at exists (hence the currentPage + 1). If it does, we've already fetched it and we quit the function. Otherwise:
else {
// view is missing, create it and set its tag to currentPage+1
}
Here, we lazy load the image. This is done, for example, by creating a new thread and downloading the image from a server. We do this while the view is not the currentPage because we don't want the image to "pop in" while the user is scrolling. The view to which we add the image gets a tag (UIView has a "tag" property); we set the tag to currentPage+1, which later allows us to index the view in case we need it.
Finally, we have:
/**
* using your paging numbers as tag, you can also clean the UIScrollView
* from no longer needed views to get your memory back
* remove all image views except -1 and +1 of the currently drawn page
*/
for ( int i = 0; i < currentPages; i++ ) {
if ( (i < (currentPage-1) || i > (currentPage+1)) && [myScrollView viewWithTag:(i+1)] ) {
[[myScrollView viewWithTag:(i+1)] removeFromSuperview];
}
}
Here, we use our currentPage variable and iterate through all our views by indexing them by the tag we set. If the tag is not one off from the currentPage (remember, we don't want any pop in!) we remove it from the scrollview and free some memory up.
Hope that helped.

Perhaps this will help you.
Downloads the Asynchronous ImageView files from here https://github.com/nicklockwood/AsyncImageView/ and include them into your project.
Drag the ImageView on xib file and change it's class to AsynchronousImageView rather than UIImageView
Write this in you .h file
IBOutlet AsynchronousImageView *_artworkImg;
Write this in your .m file
[_artworkImg loadImageFromURLString:#"Your Image Url"];

Related

Unity - How to scroll a scrollbar for a ScrollRect smoothly?

I have a ScrollRect to which I add content to. When it reaches the point where the content is longer than the ScrollRect (ie when the ScrollBar's value changes from 0), I want the ScrollRect to scroll all the way down (I do this by tweening the ScrollBar's value until it reaches 0). However, my problem is that I can't figure out how to do this smoothly over time.
Here's my code snippet:
public void Update() {
if (scrollbar.size < 1 || scrollbar.value > 0) {
LeanTween.value(scrollbar.value, 0, duration).setOnUpdate((float val) => {
if (scrollbar.value == 0) {
LeanTween.cancel(this.gameObject);
} else {
scrollbar.value = val / scrollAdjustment;
}
});
}
}
I tried using "Time.deltaTime" and "Time.time" in place of duration and it did not seem to matter. Here's a gif of what happens:
(In this example, I used "duration" that had the value of 5 (the idea was to have the transition take 5 seconds) and "scrollAdjustment" was 50 but it did not seem to matter what I set either of these values to.
You can see it instantly snaps to the bottom. I'd like this to be a smooth transition. Any help is appreciated!
My settings:
Then here is me scrolling with my mouse wheel while the autoscroll feature is turned off (because I'm using Rewired, I am intercepting an input called "ZoomIn" and "ZoomOut" and add "0.01f * scrollSpeed" (where scrollSpeed is 15 in this case):
You shouldn't need to add any additional code to make the scroll bar function as the way you want it to. Try deactivating your script and mess with your scroll bar. Make sure you have your Number Of Steps On Your Scrollbar Component as 0 to make it smooth. If it's still not working, send some screenshots of your scroll gameobject to see if the rect is the problem or something else.

Large TableView with thumbnails crashing app in Titanium

I have a table view with custom rows (77 to be exact). Each custom row has a thumbnail and a title.
The thumbnail is loaded from a sprite via:
var row = Alloy.createWidget('com.dop.vforummobile.sliderow');
//-- thumbBlob is a jpg file that is 107x6160, maybe the large sprite is the problem?
var cropped = thumbBlob.imageAsCropped({
height:80,
width:107,
x:0,
y:i * 80
});
row.init(slides[i], i, cropped);
row.init(args) is below
var init = function(data, index, cropped)
{
$.title.text = (index + 1) + '. ' + data.title;
$.slideRow.startTime = data.startTime;
$.slideRow.searchFilter = data.title + ' ' + data.slideText;
if (Ti.Platform.osname == 'android') $.slideRow.title = $.slideRow.searchFilter;
if (Ti.Platform.osname != 'android') $.slideRow.selectionStyle = Ti.UI.iPhone.TableViewCellSelectionStyle.NONE;
$.thumb.image = cropped;
};
I noticed if I don't set the thumbnail, the performance on the tableview is much greater and it doesn't crash so I know it has to do with the thumbnails.
The tableview loads fine, its when I start scrolling rapidly, the tableview begins to lag on my iPad and then it crashes.
I noticed if I scroll slow enough to get to the bottom of the tableview without it crashing and then scroll rapidly, its fine. It's like they all loaded into memory so I can scroll at any rate.
Any ideas on this? To me it seems the rows don't load until they are actually visible and if I scroll to fast, too many rows are being loaded and I run out of memory and it crashes
I think the problem is that you set the image as blob and do so every time the table is rendered. I would suggest to cut the sprite only the first time, save them to device and then pass the paths when you render the table.
To overall greatly improve list performance, switch from TableView to the newer ListView.
A few notes to improve your code:
Use OS_ANDROID instead of Ti.Platform.osname != 'android'
Use #slideRow[platform=ios] { selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE } in your widget.tss instead of styling inline in widget.js

A way to check a BufferedImage repaint()?

I'm working in Eclipse and I want to know if I can make an if statement that checks to see if the BufferedImage has been painted/drawn onto the frame. For some reason, it's not painting the correct image because clickable regions are appearing on that picture when they are not supposed to.
For example, when I click the region to go from 4>5 everything is good. When I click from 5 to go to 4 I end up at 6 because the 'regions' from 4 are appearing in 5 (The image should always be painted before the clickable regions are shown) before it's even being painted. I want to restrict this to check if the image has been painted onto the frame first.
I really don't want to use anything else besides what I have right now (so no new classes being implemented to do this task), I really just want a simple yet effective way to resolve this. Here is what I'm talking about:
...
MouseAdapter mouseHandler = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
repaint();
if(n==0)
{
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133
{
n = 4;
}
return;
}
if(n==5)
{
if(...)
{
n = 4;
}
return();
}
if(n==6)
{
if(...)
{
n = 5;
}
if(...)
{
n = 0;
}
if(...)
{
n = 6;
}
return();
}
}
...
I think you might need to give a little more information. The problem might lie in how you repaint, not whether it was painted.
If you are running another thread as your main program, you might instead want to send the mouse events synchronously to that so that the main thread can process the mouse click and then repaint.
Another solution might be to override the repaint method and paint the buffered images there, but you may have done that.
Also, a little off topic, I noticed that you used for loops to determine if the mouse was clicked in a specific area.
You could shorten the code:
for(int i=459; i<491; i++){
if(e.getX()==i){
for(int j=111; j<133; j++){
if(e.getY()==j){
//action taken
}
}
}
}
To:
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133{
//action taken
}
This would take up less space in your code and less time checking every pixel.
Back to your question.
I dont know of a function to tell if a buffered image has been painted. The ploblem that you are having though might of might not be in the code provided. Posting the rest of your code would be beneficial.
Okay I found the solution, I forgot to come back to this question and let you know. The problem was that the mouse was double clicking for some reason. You could almost say it was 'recursive'. I decided to move the mouseListener from inside the paintComponent to outside of it, and surely enough that fixed it.

iOS Autolayout - How to set two different distances between views, depends on the screen height

I know I'm missing something, because this has to be something easy to achieve.
My problem is that I have in my "loading screen" (the one that appears right after the splash) an UIImageView with two different images for 3.5" and 4" size screen. In a certain place of that images, I put one UIActivityIndicator, to tell the user that the app is loading something in the background. That place is not the same for both images, because one of them is obviously higher that the other, so I want to set an autolayout constraint that allows me to put that activity indicator at different heights, depends on if the app is running in an iPhone 5 or not.
Without Autolayout, I'd set the frame.origin.y of the view to 300 (for example), and then in the viewDidLoad method of the ViewController, I'd ask if the app is running in an iPhone 5, so I'd change the value to, for example, 350. I have no idea how to do this using Autolayout and I think it has to be pretty simple.
You can create an NSLayoutConstraint outlet on your view controller and connect the outlet to the activity indicator's Y constraint in your xib or storyboard. Then, add an updateViewContraints method to your view controller and update the constraint's constant according to the screen size.
Here's an example of updateViewConstraints:
- (void)updateViewConstraints {
[super updateViewConstraints];
self.activityIndicatorYConstraint.constant =
[UIScreen mainScreen].bounds.size.height > 480.0f ? 200 : 100;
}
Of course you will want to put in your appropriate values instead of 200 and 100. You might want to define some named constants. Also, don't forget to call [super updateViewConstraints].
The problem of #Rob answer's is you should do a lot of code for each constraint.
So to resolve that, just add ConstraintLayout class to your code and modify constraint constant value for the device that you want in the IB :
//
// LayoutConstraint.swift
// MyConstraintLayout
//
// Created by Hamza Ghazouani on 19/05/2016.
// Copyright © 2016 Hamza Ghazouani. All rights reserved.
//
import UIKit
#IBDesignable
class LayoutConstraint: NSLayoutConstraint {
#IBInspectable
var 📱3¨5_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 480 {
constant = 📱3¨5_insh
}
}
}
#IBInspectable
var 📱4¨0_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 568 {
constant = 📱4¨0_insh
}
}
}
#IBInspectable
var 📱4¨7_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 667 {
constant = 📱4¨7_insh
}
}
}
#IBInspectable
var 📱5¨5_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 736 {
constant = 📱5¨5_insh
}
}
}
}
Don't forgot to inherit your class constraint from ConstraintLayout
I will add the objective-c version soon
The basic tool in Auto Layout to manage UI objects' position is the Constraints. A constraint describes a geometric relationship between two views. For example, you might have a constraint that says:
“The right edge of progress bar is connected to the left edge of a lable 40 points of empty space between them.”
This means using AutoLayout you can't do conditional position setting based on UIDevice's mode, rather you can create a view layout which modifies itself if eg. the app runs on 3.5' full screen (IPhone4) or 4' full screen (IPhone5) based on the constraints.
So options for your problem using Constraints:
1) find a view on your layout which can be used to create a constraint to position the progressbar relatively. (select the view and the progressbar using CMD button, then use Editor/Pin/Vertical Spacing menu item to create a vertical constraint between the 2 objects)
2) create an absolute constraint to stick the progressbar's position to screen edge (keeping space) or centrally
I found helpful this tutorial about AutoLayout which might be beneficial for you also:
http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2
Pls note: autolayout only works from IOS 6.
The new way, Without writing a single line!
No need to write device based conditions like these :-
if device == iPhoneSE {
constant = 44
} else if device == iPhone6 {
constant = 52
}
I created a library Layout Helper so now you can update constraint for each device without writing a single line of code.
Step 1
Assign the NSLayoutHelper to your constraint
Step 2
Update the constraint for the device you want
Step 3
Run the app and see the MAGIC
I generally always try to stay in Interface Builder for setting up constraints. Diving in code to have more control is usually useful if you have completely different layouts on iPhone 4 and 6 for example.
As mentioned before, you can't have conditionals in Interface Builder, that's when linking a constraint to your view controller really comes handy.
Here's a short explanation on 3 approaches to solve Auto Layout issues for different screen sizes: http://candycode.io/how-to-set-up-different-auto-layout-constraints-for-different-screen-sizes/

enum in Singleton cocos2d

What do you recommend me if I have two layers (which are added to a parallaxNode) and on each layers I've added sprites which I want to restrict the order that the player won't be able to remove sprite5 until it removes first the previous ones: sprite1, sprite2, sprite3 and sprite4 and so one.
I'm using Juan's example for dynamically touch detection
What will be the dynamical option for this?
UPDATE:
if([sprite tag] == tag_collection_1 && [sprite GetCanTrack]) {
//Set others to NO & call remove sprite method
[[TSprite FindByTag:tag_collection_number] SetCanTrack:NO];
[self removeSpriteMethod:touchLocation];
} else if ([sprite tag] == tag_collection_number && [sprite GetCanTrack]) {
// and so on
}
Use a fifo stack implementation. If you want to remove the sprite, you first check if it's at the bottom of the stack and when you do remove it, you pop it from the stack as well. Practically you will have an NSMutableArray and look at the first item if the object is in that position before you remove it. If you have added 1,2,3,4,5 in that order and want only to be able to remove them in the same order you check if the object you are trying to remove is at index 0 in the array first.