Large TableView with thumbnails crashing app in Titanium - iphone

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

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.

Unity 5: how to zoom and translate an object from a grid layout to the center of the screen

I'm trying to create a scroll grid view in which every cell object is tapable.
When a cell object is tapped I want to scale and traslate it to the center of the screen and render it above other cells.
I was able to make it tapable and scale it in its position. Now I want to move the cell object to the center of the screen and render it above other cells.
I've tried many solutions but none of them works.
This is my hierarchy:
This is the grid in normal state:
This is the grid when a cell was tapped:
I'm populating the grid from a C# script dynamically.
void Populate()
{
GameObject cardContainerInstance, cardInstance;
foreach (var c in cardsCollection.GetAll())
{
if (c.IsOwned)
{
cardContainerInstance = Instantiate(cardContainer, transform);
cardInstance = cardContainerInstance.transform.Find("Card").gameObject;
var cardManager = cardInstance.GetComponent<CardManager>();
cardManager.card = c;
cardManager.AddListener(this);
}
else
{
Instantiate(cardSlot, transform);
}
}
}
public void OnCardClick(GameObject cardObject, Card card)
{
Debug.Log("OnCardClick " + card.name);
if (openedCard != null) {
if (openedCard.Number == card.Number)
{
CloseCard(openedCardObject);
}
else
{
CloseCard(openedCardObject);
OpenCard(cardObject, card);
}
}
else
{
OpenCard(cardObject, card);
}
}
void OpenCard(GameObject cardObject, Card card)
{
//cardObject.GetComponent<Canvas>().sortingOrder = 1;
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Open");
openedCard = card;
openedCardObject = cardObject;
}
void CloseCard(GameObject cardObject)
{
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Close");
openedCard = null;
openedCardObject = null;
}
I can't figure out how to move the cell to the center and render it above others.
Note that all is animated using an animator attached to the object itself.
Could anyone help me please? Thank you very much!
EDIT: more details
All cell object have the following hierarchy:
where:
CardContainer is an empty object added to use animator on Card child object
Card is the object itself that has a script, a canvas renderer and an animator
StatsImage is the object that slide out when the card is tapped
Image is a calssic UIImage with Image script, Shadow script and canvas renderer
Other component are simple texts.
EDIT: fix in progress
Trying to apply this suggestions I was able to manage the rendering order (as you see on the image below) but it seems that prevent touch events to be detected on the game object.
I've added a GraphicsRaycaster too and now the bottom horizontal scroll view scrolls again but only if I click and drag a card.
Moreover, with the GraphicsRaycaster, the main grid card still are not clickable and it's possible to open the card only if it is behind the bottom panel (if I click on the red spot in the image below the card behind the panel receives che click)
This is the CardContainer at runtime(note that I'm attaching new Canvas and GraphicsRaycaster on the CardContainer, which is the "root" element):
You didn't clarify whether you are using a sprite renderer or some other method but here is an answer for each.
Sprite renderer:
this the simple one. In each sprite renderer, there is a variable called "sortingOrder" in script and "Order in layer" in the inspector. sprite renderer with sorting Orders that are higher is rendered first. All you would need to do is call:
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 1;
when you click the card, and
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 0;
when you unclick it. I hope that makes sense!
Other Method:
this one is a bit harder and I would suggest that you switch to sprite renderers and it will be much easier and more stable down the road, but I can understand if you have already written a lot of scripts and don't want to go back and change them.
Anyway, all you will need to do Is create two layers: cardLower and cardUpper. then create a new camera and call it topCamera. now under the top camera object in the inspector, change the culling mask (it's near the top) and make sure cardUpper is selected. then change the Clear flags (first one) to "Don't Clear" finally change the depth to 0 (if that doesn't work change it to -2). Now objects in the cardUpper will always be rendered above everything else. You can change the layer through script with
cardObject.layer = "cardUpper"
or
cardObject.layer = "cardLower"
I hope that helps!
Ok, so its pretty simple. So you are going to want to add another canvas component to the game object, and check the override sorting to true. Then use
cardObject.GetComponent<Canvas>().sortingOrder = 1;
to place it in the front and
cardObject.GetComponent<Canvas>().sortingOrder = 0;
to put it in the back.
you are also going to need to put a GraphicsRaycaster on to each of the cardObjects
Ignore my other answer about sprite renderers, they are not needed here

black boxes instead of sprites on reload andengine

My sprites (which are boxes themselves, but different colors) are all showing as black boxes the second time my andengine activity is loaded. It's odd because usually this problem is due to the texture atlas not being large enough, but I tried doubling the size of the atlas, and this didn't work. So here's the relevant code, any help would be much appreciated!
So to put it out there, the first time I load my game, everything is fine and perfect, but the second time, the sprites appear black.
I've seen other questions where the sprites appear as black boxes but for me they load fine the first time, which does not happen for other questions, and the answers which were given on the other questions did not work for me (they were, atlas needs to be power of two, and atlas is not big enough)
public void loadGameResources(){
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
gameTextureAtlas = new BuildableBitmapTextureAtlas(activity.getTextureManager(), 512, 512, TextureOptions.BILINEAR);
blackTile = BitmapTextureAtlasTextureRegionFactory.createFromAsset(gameTextureAtlas, activity, "blacktile.png");
greyTile = BitmapTextureAtlasTextureRegionFactory.createFromAsset(gameTextureAtlas, activity, "greytile.png");
redTile = BitmapTextureAtlasTextureRegionFactory.createFromAsset(gameTextureAtlas, activity, "redtile.png");
greenTile = BitmapTextureAtlasTextureRegionFactory.createFromAsset(gameTextureAtlas, activity, "greentile.png");
Log.d(gameTextureAtlas+"","didn'tignore");
loadFont();
try{
Log.d("LOPOLL","arrived");
gameTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 1, 0));
Log.d("boom","arrived");
gameTextureAtlas.load();
Log.d("gotHere","arrived");
}catch(Exception e){
Log.d("WrongPlace","arrived");
}
}
(Resource unload method)
public void unloadGameResources(){
if(gameTextureAtlas !=null){
gameTextureAtlas.unload();
}
gameTextureAtlas = null;
// blackTile = null;
// greyTile = null;
// redTile = null;
// greenTile = null;
}
The reason that I commented the section giving the tiletextures a null value is that doing this for some reason created a nullexception error even though I thought the textureregions would be reassigned textures upon reloading the activity as reloading the activity calls the loadgameresources() method
Got the answer guys.. System.exit(code) ensures the activity is exited properly. Dissapointed stack overflow was not able to answer this!

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/

iOS UIScrollView Lazy Loading

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"];