UIScrollView with many subviews takes time to process - ios

I have an UIScrollView with subviews. It contains a result list of products/items. UIViewwith UIImageView and UILabel. It may contain many items. My problem is when I'm pressing the button(Grid Button) to change the UI result into grid view, it takes time before it finishes the processing. Is there a way to make this faster? Like remove images which are not visible in screen/UIScrollView?
Here's my code for listView
-(void)actionList {
NSLog(#"List!!");
isGrid = NO;
for(UIView*iView in sv_results.subviews){
for(UILabel *iView2 in iView.subviews){
if([iView2 isMemberOfClass:[UILabel class]]){
if(iView2.tag == 0)
{
iView2.frame = CGRectMake(80, 0, 150, 50);
iView2.font = [UIFont boldSystemFontOfSize:12.0f];
}
else if(iView2.tag == 1)
{
pricewidth = [iView2.text sizeWithAttributes:#{NSFontAttributeName:[UIFont systemFontOfSize:13.0f]}];
iView2.frame = CGRectMake(80, 20, pricewidth.width+1, 50);
iView2.font = [UIFont systemFontOfSize:13.0f];
for(UIView *discountline in iView2.subviews)
{
discountline.frame = CGRectMake(0, iView2.frame.size.height/2, iView2.frame.size.width, 1);
}
}
else if(iView2.tag == 2)
{
iView2.frame = CGRectMake(screenRect.size.width-60, 30, 50, 15);
iView2.font = [UIFont systemFontOfSize:10.0f];
}
else if(iView2.tag == 3)
{
iView2.frame = CGRectMake(140, 20, 150, 50);
iView2.font = [UIFont systemFontOfSize:13.0f];
}
else {
iView2.frame = CGRectMake(80, 0, 150, 50);
iView2.font = [UIFont boldSystemFontOfSize:12.0f];
}
}
if([iView2 isMemberOfClass:[AsyncImageView class]]){
iView2.frame = CGRectMake(15,12,50,50);
iView2.layer.cornerRadius = 5;
}
}
for(int i = 0;i<=allAvailableData;i++)
{
if(iView.tag == i)
{
iView.frame = CGRectMake(0,((i-1)*75),320,75);
}
//sv_results.contentSize = CGSizeMake(320,yPosition);//optional resize height scrollview from gridview
}
iView.layer.borderWidth = .3f;
}
sv_results.contentSize = CGSizeMake(320,(allAvailableData)*75);
}

It can take a time because of images. If you are loading a lots of images in main thread then it is normal to take a time. Try to use lazy image loading with UITableView. I use this in my project and easy to implement and use. http://www.theappguruz.com/sample-code/ios-lazy-loading-images/ there is some codes and helps. I did not check this but it looks also fine https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html

Just use
scrollView.delaysContentTouches = NO;
A Boolean value that determines whether the scroll view delays the handling of touch-down gestures.
If the value of this property is YES, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is NO , the scroll view immediately calls touchesShouldBegin:withEvent:inContentView:. The default value is YES.
This means, scrollView will delay touches by default, you can disable that functionality by above code,
Hope it will solve your problem.

I think you should implement UITableView and you can load image with "dispatch_async" so it will not take time. I hope you may get solution this way.

Related

iOS Chips in UITableViewCell with dynamic height

I have tried tirelessly to create a "chips" layout within a table view cell where the height of the cell expands to fill the contents but with no luck.
My research has landed me with this library https://github.com/zoonooz/ZFTokenField but it doesn't quite fill my needs. An example is like the Messages app where the "to" field expands to fill all the contacts, or primarily this image below is what I'm ultimately attempting to achieve:
Where the chips wrap to the next line where appropriate expanding the cell as it goes. The best I have so far is "sort of" working minus the cell height and the label at the start (label should be an easy fix so not the main concern)
I've tried many approaches but with no cigar and any help would be greatly appreciated. (each "chip" needs to be a button so I can tap to remove them). I've tried with a collection view in the table view cell (which I didn't like) and manually laying out each button (show below)
- (CGSize)updateChipsLayout {
self.minWidth = 0.0f;
self.minHeight = 0.0f;
if (!self.visibleButtons) {
self.visibleButtons = [NSMutableArray new];
}
for (UIButton *button in self.visibleButtons) {
[button removeFromSuperview];
}
[self.visibleButtons removeAllObjects];
CGRect oldButtonRect = CGRectZero;
oldButtonRect.origin.x = self.buttonPadding;
oldButtonRect.origin.y = self.buttonPadding;
CGFloat maxHeight = 0.0f;
for (ChipsData *data in self.chips) {
NSString *autoLayout_orientation = #"H";
NSLayoutFormatOptions autoLayout_options = NSLayoutFormatAlignAllLeft;
UIButton *button = [data generateButton];
CGSize buttonSize = button.bounds.size;
CGFloat newX = oldButtonRect.origin.x + oldButtonRect.size.width + self.buttonPadding;
if (newX + buttonSize.width > self.bounds.size.width) {
newX = self.buttonPadding;
oldButtonRect.origin.x = newX;
oldButtonRect.origin.y += maxHeight + self.buttonPadding;
maxHeight = 0.0f;
autoLayout_orientation = #"V";
autoLayout_options = NSLayoutFormatAlignAllTop;
}
if (buttonSize.height > maxHeight) {
maxHeight = buttonSize.height;
}
self.minHeight = MAX(self.minHeight, buttonSize.height);
self.minWidth = MAX(self.minWidth, buttonSize.width);
button.frame = CGRectMake(
newX,
oldButtonRect.origin.y,
button.frame.size.width,
button.frame.size.height
);
button.tag = self.visibleButtons.count;
[button addTarget:self action:#selector(buttonTouchUp:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
oldButtonRect = button.frame;
[self.visibleButtons addObject:button];
}
self.cellSize = CGSizeMake(
oldButtonRect.origin.x + oldButtonRect.size.width + self.buttonPadding,
oldButtonRect.origin.y + maxHeight + self.buttonPadding
);
//[self setHeight:self.cellSize.height];
[self invalidateIntrinsicContentSize];
return self.frame.size;
}
The button generation logic isn't that complex either:
- (UIButton *)generateButton {
UIColor *backgroundColor = [UIColor colorWithWhite:0.1f alpha:1.0f];
UIColor *backgroundColorHighlighted = [UIColor colorWithWhite:0.4f alpha:1.0f];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
[attrText addAttributes:#{
NSForegroundColorAttributeName: [UIColor whiteColor],
NSBackgroundColorAttributeName: backgroundColor,
}
range:NSMakeRange(0, attrText.length)
];
[button setAttributedTitle:attrText forState:UIControlStateNormal];
[button setBackgroundImage:[self generateBackgroundWithColor:backgroundColor] forState:UIControlStateNormal];
[button setBackgroundImage:[self generateBackgroundWithColor:backgroundColorHighlighted] forState:UIControlStateHighlighted];
[button sizeToFit];
[button.layer setCornerRadius:8.0f];
[button.layer setMasksToBounds:YES];
[button setClipsToBounds:YES];
return button;
}
I assume I'll need a function to determine the cell height outside of the cell as I can't get a reference to the cell to determine it's height until I tell it what it's height should be.
PS yes I am aware that I am still using Objective-C when Swift is available.
The size of the cell is not determined by the cell, but by the tableView's delegate, specifically
tableView:heightForRowAtIndexPath:
So, you'll have to get the size of how high each cell will be and return that height. Also, if the data for each item in the cell does not change while the user is in that view, I would recommend storing the height for each individual index in an array once it is known so you can avoid expensive calculations.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// We are just going to assume a single section for brevity
// And we also have an array of CGFloats that is the same size as the # of rows
CGFloat height = knownHeights[indexPath.row];
if (height == 0) // our default value when we created the array
{
// You'll have to write the get custom height function
height = [self getHeightForItemAtIndexPath:indexPath];
knownHeights[indexPath.row] = height;
}
return height;
}

Grouped Bar chart with JBChartView library

I've needed to draw a grouped bar charts in an iOS Projects I'm doing. So the iOS Third party library I used is JBChartView from Jawbone.
Up to the knowledge I gained with my research regarding this I found no any already supported ways within the library.
However I tried doing something like as follows:
- (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView
{
return [self.chartLegend count]; // number of bars in chart "BB", "HB", "FB", "RO"
}
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index
{
CGFloat height = (isnan((([[chartData objectAtIndex:index] doubleValue]/total)*100))) ? 0.0 : (([[chartData objectAtIndex:index] doubleValue]/total)*100);
NSLog(#"height : %f", height);
return height;
}
- (UIView *)barChartView:(JBBarChartView *)barChartView barViewAtIndex:(NSUInteger)index {
CGFloat height = (isnan((([[chartData objectAtIndex:index] doubleValue]/total)*100)))
? 0.0
: (([[chartData objectAtIndex:index] doubleValue]/total)*100);
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 50, height)];
[view setBackgroundColor:[UIColor grayColor]];
UIView *firstHalf = [[UIView alloc] initWithFrame:CGRectMake(0, height, 25, height)];
[firstHalf setBackgroundColor:[UIColor redColor]];
UIView *secondHalf = [[UIView alloc] initWithFrame:CGRectMake(25, height, 25, height/2)];
if (index == 0) {
[secondHalf setBackgroundColor:UIColorFromRGB(0xF15854)];
} else if (index == 1) {
[secondHalf setBackgroundColor:UIColorFromRGB(0x5DA5DA)];
} else if (index == 2) {
[secondHalf setBackgroundColor:UIColorFromRGB(0xFAA43A)];
} else if (index == 3) {
[secondHalf setBackgroundColor:UIColorFromRGB(0x60BD68)];
}
[view addSubview:firstHalf];
[view addSubview:secondHalf];
return view;
}
Note: Please, ignore the data values which I've put to determine the heights of the Red color and other varying color bars. And UIColorFromRGB is a custom methods I use in my development.
But it gives the graph as follows:
But what I really want to draw using JBChartView library is as follows:
Any help is accepted with loads of gratitude and respect, but except don't suggest to use any other library to get this done. I like the simplicity of JBChartView library. :-)
Thanks in advance!
From what I've found out, this is not possible using standard way, but can be worked around. See https://github.com/Jawbone/JBChartView/issues/139

How to add a another image to Thumb of a slider or How to add two thumb images?

I am Building ios app using storyboards.I am facing a critical issue now. I created slider programmatically. I added thumb image as my own custom image. I want to increase and decrease width on button click. These all are working fine. Only thing I want to do is, I want to add an another image under this thumb image. The image newly added should move respective with thumb image.
This method will create lot of complications.Better create custom slider.Everything have to be created programmatically.
Declare views and frames as shown Below.
UIView *slider,*timeLine,*mainView;
UIView *thumb;
CGRect sliderFrame,thumbFrame;
-(void)createTimeLine
{
timeLine = [[UIView alloc]initWithFrame:CGRectMake(0, 0, mainView.frame.size.width, 10)];
timeLine.backgroundColor=[UIColor colorWithRed:224.0/255.0f green:224.0/255.0f blue:224.0/255.0f alpha:1.0];
[mainView addSubview:timeLine];
[self createSlider];
[self createThumb];
}
-(void)createSlider
{
sliderFrame=CGRectMake(50, 0, delta, 10);
slider=[[UIView alloc]initWithFrame:sliderFrame];
slider.backgroundColor=[UIColor colorWithRed:0.0/255.0f green:102.0/255.0f blue:0.0/255.0f alpha:1.0];
[mainView addSubview:slider];
[slider bringSubviewToFront:mainView];
}
-(void)createThumb{
UIImage * thumbImage = [UIImage imageNamed:#"games#2x.png"];
float actualHeight = thumbImage.size.height;
float actualWidth = thumbImage.size.width;
thumbFrame = CGRectMake((fabs(actualWidth - sliderFrame.size.width))/2,timeLine.frame.size.height,actualWidth,actualHeight);
thumb=[[UIView alloc]initWithFrame:thumbFrame];
thumb.center = CGPointMake(sliderFrame.origin.x + sliderFrame.size.width/2, sliderFrame.size.height+thumbFrame.size.height/2);
thumb.backgroundColor= [UIColor colorWithPatternImage:thumbImage];
thumb.userInteractionEnabled = YES;
[mainView insertSubview:thumb atIndex:mainView.subviews.count];
UIPanGestureRecognizer *move=[[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(drag:)];
move.maximumNumberOfTouches = 1;
move.minimumNumberOfTouches = 1;
[thumb addGestureRecognizer:move];
}
-(void)drag:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint touchLocation = [panGestureRecognizer locationInView:mainView];
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) {
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
if ((touchLocation.x>(timeLine.frame.origin.x-(thumbFrame.size.width/2))) && (touchLocation.x <= (timeLine.frame.size.width-(thumbFrame.size.width/2))))
{
thumbFrame.origin.x =touchLocation.x;
thumb.frame = thumbFrame;
sliderFrame.origin.x = touchLocation.x + (thumbFrame.size.width - sliderFrame.size.width)/2;
slider.frame = sliderFrame;
}
} else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
}
}
Pangesture is applied for moving the thumbImage.

UIViews in scrollviews

I have a UIScrollview with UIView, and in every UIView has a labels and image. I'm looping this view and it's working fine, but I have a button Grid and when i press this they changed into grid view. The problem is the last item is not working fine as the others. One of the labels are hidden in the last item. And when I pressed the list view button, the last item has a problem again, the labels are complete but the 'View' didn't changed.
-update.
the content size is okay, and it's leaving a space for the last item but the last item is somewhere. I can't post images yet because of the reputation.
`-(void)actionList {
int prods = 0;
isGrid = NO;
for(UIView*iView in sv_results.subviews){
for(UILabel *iView2 in iView.subviews){
if([iView2 isMemberOfClass:[UILabel class]]){
if(iView2.tag == 0)
{
iView2.frame = CGRectMake(80, 0, 150, 50);
iView2.font = [UIFont boldSystemFontOfSize:12.0f];
NSLog(#"%#",iView2.text);
}
else if(iView2.tag == 1)
{
pricewidth = [iView2.text sizeWithAttributes:#{NSFontAttributeName:[UIFont systemFontOfSize:13.0f]}];
iView2.frame = CGRectMake(80, 20, pricewidth.width+1, 50);
iView2.font = [UIFont systemFontOfSize:13.0f];
for(UIView *discountline in iView2.subviews)
{
discountline.frame = CGRectMake(0, iView2.frame.size.height/2, iView2.frame.size.width, 1);
}
}
else if(iView2.tag == 2)
{
iView2.frame = CGRectMake(screenRect.size.width-60, 30, 50, 15);
iView2.font = [UIFont systemFontOfSize:10.0f];
}
else if(iView2.tag == 3)
{
iView2.frame = CGRectMake(140, 20, 150, 50);
iView2.font = [UIFont systemFontOfSize:13.0f];
}
}
if([iView2 isMemberOfClass:[AsyncImageView class]]){
iView2.frame = CGRectMake(15,12,50,50);
iView2.layer.cornerRadius = 5;
}
}
NSLog(#"all avail data > %i",allAvailableData);
for(int i = 0;i<allAvailableData;i++)
{
if(iView.tag == i)
{
NSLog(#"UIView tag > %i",iView.tag);
iView.frame = CGRectMake(0,((i-1)*75),320,75);
}
}
prods++;
iView.layer.borderWidth = .3f;
}
NSLog(#"%i",prods);
sv_results.contentSize = CGSizeMake(320,(allAvailableData)*75);
}`

Fill view with buttons at random positions

i want to fill area (UIView) with buttons(UIButton) so that they do not intersect with each others.
My idea:
create initial button at random position in view;
fill view with other buttons from initial button (count < 20) that they do not intersect ~10 pixels from each other.
What have i done so far:
I created method:
-(void)generateButtonsForView:(UIView *)view buttonCount:(int)count
{
//get size of main view
float viewWidth = view.frame.size.width;
float viewHeight = view.frame.size.height;
//set button at random position
UIButton *initialButton = [[UIButton alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth,
arc4random() % (int)viewHeight,
buttonWidth, buttonHeight)];
[initialButton setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
[view addSubview:initialButton];
// set count to 20 - max number of buttons on screen
if (count > 20)
count = 20;
//fill view with buttons from initial button +- 10 pixels
for (int i=0;i<=count;i++)
{
//button
UIButton *otherButtons = [[UIButton alloc] init];
[otherButtons setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
...//have no idea what to do here
}
}
So i'm confused on the place where i want to generate the position of the other buttons, depending on the initial button. I do not know how to generate theirs position that they were at a distance of 5-10 pixels from each other... Any ideas how to realise this? Thanks!
Here's an example with views rather than buttons, but the concept is the same. I use CGRectInset to give the new potential view a buffer of 10 points around it, then look for whether the new view intersects any of the other views. If there's no intersection, add the subview, if there is, try again with a new random location.
-(void)generateButtonsForView {
float viewWidth = self.view.frame.size.width;
float viewHeight = self.view.frame.size.height;
UIView *initialView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
initialView.backgroundColor = [UIColor redColor];
[self.view addSubview:initialView];
int numViews = 0;
while (numViews < 19) {
BOOL goodView = YES;
UIView *candidateView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
candidateView.backgroundColor = [UIColor redColor];
for (UIView *placedView in self.view.subviews) {
if (CGRectIntersectsRect(CGRectInset(candidateView.frame, -10, -10), placedView.frame)) {
goodView = NO;
break;
}
}
if (goodView) {
[self.view addSubview:candidateView];
numViews += 1;
}
}
}

Resources