Dailycode.info

Short solution for short problems

iOS: UITable view (Server Side) Lazy Loading

I'm creating an app that will be used in factories. At the test factories, the application runs fast and smooth. But when starting to deploy to other factories where the workflow is different, I had some grids with loading times of 5 to 7 seconds. They didn't liked it and so did I. So then I had to build in paging in the grids. Since I'm working with a .Net backend server, I will post this answer using LINQ on the server and iOS on the app.

The solution is to show the first 50 elements in the grid, when the 50st element is shown, he has to get the next 50 element and so on... The implementation is actually very simple. I pass the itemCount and the pageSize to the server. So you will need to add these arguments to the getter of your data. On server side I just add a simple Skip and Take using link. Skip the first ... items and Take the (pageCount) number of items. Thats it. So it took me 30 minutes to implement it for the first grid, the next will be 5 minutes of work. 

First on the app side, add this variable

int iPageSize=0;

Then in the CellForRowAtIndexPath method you''l need to check the number of items and load the following items before you return the cell. The store.PageItemCount is a global variable that defines the max items per page, I made it global so it can be used on all grids and variable so we can change the item size on the device.

int indexRow = indexPath.row+1;

int iPageSizeSum = iPageSize+store.PageItemCount;

if (indexRow >= iPageSizeSum)

{

    iPageSize = iPageSize + store.PageItemCount;

    [self configureView];

}

 

in the configureView I add the items to a mutable array and reload the table:

    

[self.ordersOnJig addObjectsFromArray:(NSMutableArray *) value];

[self.locationsTable reloadData];

[self.locationsTable reloadInputViews];

Now the server side integration is even more simple:

returnList = returnList.Skip(skip).Take(pageSize).ToList();

return returnList;



iOS: Nice popup menu havely based on LeveyPopListView

I the ActionSheet menu just isn't cool enough for you, then try this. I got the code form Levey. Just adapted the sizing of the popup, and that's it.

First a teaser that looks like this:

 

The code is very simple. It will create a rectangle, show a title header on top and a UITableView on the bottom. You can use the Cell layout from the table view, lke adding images and subtitles very easy. You can download the code here: https://github.com/levey/LeveyPopListView

The only small chagens I make was the color and the size and adding an image on the far right of the title. Changes made in: LeveyPopListView.m and the LeveyPopListView.h

This reflects in the change of the 2 initializers where I add the image name to use for the top image. In the implementation file I put a fixed size, but you could also provide the size with the initializer for example. 

The first thing you will need to do is to fill an Array with NSDictionaries that contain the image and text for each item. In this example I add all items from the UITabbarcontroller and add 2 extra items, cancel and logout. I call this method on the ViewLoad:

-(void) FillShortMenuOptions

{

    NSMutableArray * optionsToSet = [[NSMutableArray alloc] init];

    for (UIViewController * viewController inself.viewControllers)

    {

        NSString * test = viewController.tabBarItem.title;

        UIImage * image = viewController.tabBarItem.image;

        [optionsToSet addObject:[NSDictionary dictionaryWithObjectsAndKeys:image,@"img",test,@"text", nil]];

    }

    [optionsToSet addObject:[NSDictionarydictionaryWithObjectsAndKeys:[UIImageimageNamed:@"113-navigation.png"],@"img",[[VariableStoresharedInstance] Translate:@"$MOB$Cancel"],@"text", nil]];

    [optionsToSet addObject:[NSDictionarydictionaryWithObjectsAndKeys:[UIImageimageNamed:@"54-lock.png"],@"img",[[VariableStoresharedInstance] Translate:@"$MOB$LogOut"],@"text", nil]];

    _options = [NSArray arrayWithArray:optionsToSet];

}

To use this control just show it on any event, for example a button click event:

lplv = [[LeveyPopListViewalloc] initWithTitle:[store Translate:@"$MOB$Menu"] options:_optionsheaderImage:@"53-house.png"];

lplv.delegate=self;

[lplvshowInView:self.viewanimated:YES];

Then Implement the delegate:

UIViewController <LeveyPopListViewDelegate>


#pragma mark - LeveyPopListView delegates

- (void)leveyPopListView:(LeveyPopListView *)popListView didSelectedIndex:(NSInteger)anIndex

{

    //Put click logic here

    NSDictionary * item = _options[anIndex];

    NSString * itemText = item[@"text"];

}

- (void)leveyPopListViewDidCancel

{

    //Put cancel logic here

}

 

Here are the 2 changed files, first the .h and then the .m:

//

//  LeveyPopListView.h

//  LeveyPopListViewDemo

//

//  Created by Levey on 2/21/12.

//  Copyright (c) 2012 Levey. All rights reserved.

//  Modified by TheRave777 on 4/5/2012

//

 

 

@protocolLeveyPopListViewDelegate;

@interface LeveyPopListView : UIView <UITableViewDataSource, UITableViewDelegate>

 

@property (nonatomic, weak) id<LeveyPopListViewDelegate> delegate;

@property (copy, nonatomic) void(^handlerBlock)(NSInteger anIndex);

 

// The options is a NSArray, contain some NSDictionaries, the NSDictionary contain 2 keys, one is "img", another is "text".

- (id)initWithTitle:(NSString *)sTitle options:(NSArray *)aOptions headerImage:(NSString *) sHeaderImageName;

- (id)initWithTitle:(NSString *)sTitle

            options:(NSArray *)aOptions headerImage:(NSString *) sHeaderImageName

            handler:(void (^)(NSInteger anIndex))aHandlerBlock;

 

// If animated is YES, PopListView will be appeared with FadeIn effect.

- (void)showInView:(UIView *)aView animated:(BOOL)animated;

@end

 

@protocol LeveyPopListViewDelegate <NSObject>

- (void)leveyPopListView:(LeveyPopListView *)popListView didSelectedIndex:(NSInteger)anIndex;

@optional

- (void)leveyPopListViewDidCancel;

@end

 

 

//

//  LeveyPopListView.m

//  LeveyPopListViewDemo

//

//  Created by Levey on 2/21/12.

//  Copyright (c) 2012 Levey. All rights reserved.

//  Modified by TheRave777 on 4/5/2012

//

 

#import "LeveyPopListView.h"

#import "LeveyPopListViewCell.h"

 

#define POPLISTVIEW_SCREENINSET 40.

#define POPLISTVIEW_HEADER_HEIGHT 50.

#define POPLISTVIEW_WIDTH 400.

#define POPLISTVIEW_HEIGHT 500.

#define RADIUS 5.

 

@interface LeveyPopListView (private)

- (void)fadeIn;

- (void)fadeOut;

@end

 

@implementation LeveyPopListView {

    UITableView *_tableView;

    NSString *_title;

    NSArray *_options;

    NSString * _headerImageName;

}

 

#pragma mark - initialization & cleaning up

- (id)initWithTitle:(NSString *)sTitle options:(NSArray *)aOptions headerImage:(NSString *) sHeaderImageName{

    CGRect rect = [[UIScreenmainScreen] applicationFrame];

    if (self = [super initWithFrame:rect]) {

        self.backgroundColor = [UIColorclearColor];

        _title = [sTitle copy];

        _headerImageName = [sHeaderImageName copy];

        _options = [aOptions copy];

        

        _tableView = [[UITableViewalloc] initWithFrame:CGRectMake(POPLISTVIEW_SCREENINSET

                                                                   POPLISTVIEW_SCREENINSET + POPLISTVIEW_HEADER_HEIGHT

                                                                   POPLISTVIEW_WIDTH,

                                                                   POPLISTVIEW_HEIGHT)];

        _tableView.separatorColor = [UIColorcolorWithWhite:0alpha:.2];

        _tableView.backgroundColor = [UIColorclearColor];

        _tableView.dataSource = self;

        _tableView.delegate = self;

        [self addSubview:_tableView];

    }

    return self;    

}

 

- (id)initWithTitle:(NSString *)sTitle

            options:(NSArray *)aOptions headerImage:(NSString *) sHeaderImageName 

            handler:(void (^)(NSInteger anIndex))aHandlerBlock {

    

    if(self = [self initWithTitle:sTitle options:aOptions headerImage:sHeaderImageName])

        self.handlerBlock = aHandlerBlock;

    

    returnself;

}

 

 

#pragma mark - Private Methods

- (void)fadeIn {

    self.transform = CGAffineTransformMakeScale(1.3, 1.3);

    self.alpha = 0;

    [UIViewanimateWithDuration:.35animations:^{

        self.alpha = 1;

        self.transform = CGAffineTransformMakeScale(1, 1);

    }];

 

}

- (void)fadeOut {

    [UIViewanimateWithDuration:.35animations:^{

        self.transform = CGAffineTransformMakeScale(1.3, 1.3);

        self.alpha = 0.0;

    } completion:^(BOOL finished) {

        if (finished) {

            [selfremoveFromSuperview];

        }

    }];

}

 

#pragma mark - Instance Methods

- (void)showInView:(UIView *)aView animated:(BOOL)animated {

    [aView addSubview:self];

    if (animated) {

        [self fadeIn];

    }

}

 

#pragma mark - Tableview datasource & delegates

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return_options.count;

}

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *cellIdentity = @"PopListViewCell";

    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentity];

    if (!cell)

        cell = [[LeveyPopListViewCellalloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:cellIdentity];

    

    if ([_options[indexPath.row] respondsToSelector:@selector(objectForKey:)]) {

        cell.imageView.image = _options[indexPath.row][@"img"];

        cell.textLabel.text = _options[indexPath.row][@"text"];

    } else

        cell.textLabel.text = _options[indexPath.row];

    

    return cell;

}

 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    

    // tell the delegate the selection

    if ([_delegate respondsToSelector:@selector(leveyPopListView:didSelectedIndex:)])

        [_delegateleveyPopListView:selfdidSelectedIndex:[indexPath row]];

    

    if (_handlerBlock)

        _handlerBlock(indexPath.row);

    

    // dismiss self

    [self fadeOut];

}

#pragma mark - TouchTouchTouch

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    // tell the delegate the cancellation

    if ([_delegate respondsToSelector:@selector(leveyPopListViewDidCancel)])

        [_delegateleveyPopListViewDidCancel];

    

    // dismiss self

    [self fadeOut];

}

 

#pragma mark - DrawDrawDraw

- (void)drawRect:(CGRect)rect {

 

     CGRect titleRect = CGRectMake(POPLISTVIEW_SCREENINSET + 10, POPLISTVIEW_SCREENINSET + 10 + 5,

     400, 30);

     CGRect separatorRect = CGRectMake(POPLISTVIEW_SCREENINSET, POPLISTVIEW_SCREENINSET + POPLISTVIEW_HEADER_HEIGHT - 2,400, 2);

     

     CGContextRef ctx = UIGraphicsGetCurrentContext();

     

     // Draw the background with shadow

     CGContextSetShadowWithColor(ctx, CGSizeZero, 6., [UIColorcolorWithRed:13/255.0fgreen:90/255.0fblue:168/255.0falpha:.75].CGColor);

     [[UIColorcolorWithRed:13/255.0fgreen:90/255.0fblue:168/255.0falpha:.75] setFill];

     

     

     float x = POPLISTVIEW_SCREENINSET;

     float y = POPLISTVIEW_SCREENINSET;

     float width = POPLISTVIEW_WIDTH;

    float height = POPLISTVIEW_HEIGHT + POPLISTVIEW_SCREENINSET;

    CGMutablePathRef path = CGPathCreateMutable();

CGPathMoveToPoint(path, NULL, x, y + RADIUS);

CGPathAddArcToPoint(path, NULL, x, y, x + RADIUS, y, RADIUS);

CGPathAddArcToPoint(path, NULL, x + width, y, x + width, y + RADIUS, RADIUS);

CGPathAddArcToPoint(path, NULL, x + width, y + height, x + width - RADIUS, y + height, RADIUS);

CGPathAddArcToPoint(path, NULL, x, y + height, x, y + height - RADIUS, RADIUS);

CGPathCloseSubpath(path);

CGContextAddPath(ctx, path);

    CGContextFillPath(ctx);

    CGPathRelease(path);

    

    // Draw the title and the separator with shadow

    CGContextSetShadowWithColor(ctx, CGSizeMake(0, 1), 0.5f, [UIColorblackColor].CGColor);

    [[UIColorwhiteColor] setFill];

    [_titledrawInRect:titleRect withFont:[UIFontsystemFontOfSize:17.]];

    CGRect titleImageRect = CGRectMake(392, POPLISTVIEW_SCREENINSET + 10, 25, 25);

    [[UIImageimageNamed:_headerImageName] drawInRect:titleImageRect blendMode:nilalpha:0.5];

    CGContextFillRect(ctx, separatorRect);

}

 

@end

 
Full code can be found here!