Dailycode.info

Short solution for short problems

iOS: Picture editor (zooming, drawing, ...) complete code

I needed a view controller that could edit an image. But not just edit, also zoom in and out on the image and moving the image in the canvas.

So I created a separate XIB file with view and a view controller. It's working perfect. The first version was without zoom, so it resized the image to the screen size. In this case we always lost the resolution of the image.

So first a little demo with some pictures:

After taking the picture with the camera, a thumbnail appears in the picture list. By clicking on it, we are redirected to the picture edit screen. This is done on the 'didSelectItemAtIndexPath' method of the collection view (code see below following picture):

 

The code to accomplish this is reasonably simple. I get the asset based on the picture URL that is saved in the collection view item:

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{

    if(showZones==NO)

    {

        ImageThumbView * cell = (ImageThumbView *)[collectionView cellForItemAtIndexPath:indexPath];

        typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);

        typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);

 

        Picture * p = [picturesForProject objectAtIndex:indexPath.row];

        

        ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)

        {

            ALAssetRepresentation *rep = [myasset defaultRepresentation];

            CGImageRef iref = [rep fullResolutionImage];

 

            if (iref)

            {

                @try {

                    self.img = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];

                    [StoryBoardNavigation NavigateToChangePictureStoryboard:selfAndPicture:self.imgAndPictureObject:cell.pic];

                }

                @catch (NSException *exception) {

                    NSLog(@"Exception:%@",exception);

                    [cell.imgImage setImage:[UIImage imageNamed:@"error.png"]];

                }

            }

            else

            {

                [cell.imgImage setImage:[UIImage imageNamed:@"error.png"]];

            }

        };

        

        ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)

        {

            NSLog(@"can't get image");

        };

        NSURL *asseturl = [NSURL URLWithString:p.picture_url];

        [assetslibrary assetForURL:asseturl

                       resultBlock:resultblock

                      failureBlock:failureblock];

    }

 

}

 

Here the code to call the picture edit, so here you actually provide the PictureEdit view controller with a image object:

+(void) NavigateToChangePictureStoryboard:(UIViewController *) vc AndPicture:(UIImage *) image AndPictureObject:(Picture *) pic

{

    PictureEditVC * vcTo = [[PictureEditVCallocinitWithNibName:@"PictureEditVC"bundle:nil];

    vcTo.imageForMainView = image;

    vcTo.pic = pic;

    [UIViewtransitionWithView:vc.viewduration:0.8options:UIViewAnimationOptionTransitionCrossDissolve

                    animations:^{

                        [vc.navigationController pushViewController:vcTo animated:NO];

                    }

                    completion:NULL];

 

 

}

Now we end up on the picture edit screen. On top you see 3 buttons, the first on the left saves the images if there were changes made. The second on the left cancels the changes. The button on the right switches between zooming and editing. Below the zooming mode is set, you can zoom by using the pinch gesture and you can move the picture with the pan gesture.

Before we edit, we click on the edit button, and you'll see the different color buttons appear. 

We click on the red button and draw the little red heart on the image.

Now lets zoom in. Click on the edit button to go to zoom mode and zoom in:

Now we click again on the edit button to edit and draw some yellow hearts:

Zoom out again and save:

If you look closely the thumbnail reflects the changes. A copy of the changed image is also saved in the library.

Before we look into the code of the view controller that does all the work, first thing is to create a XIB.

Create the XIB file with the view controller. The xib file is very simple and could look like this:

 

I provide 2 properties (Basically you only will need the first). The first is the image to edit, the second is the picture object with the url of the picture (I use this to store the picture reference in the local DB), if we change the picture, we will change the picture objects url.

The .h file of the picture edit view controller is very simple:

//

//  PictureEditVC.h

//  LocationReport

//

//  Created by Mark Deraeve on 03/06/14.

//  Copyright (c) 2014 AssistU. All rights reserved.

//

 

#import <UIKit/UIKit.h>

#import "Button.h"

#import "Picture.h"

 

@interface PictureEditVC : HomeVC <UIScrollViewDelegate>

 

@property (weak, nonatomic) IBOutlet Button *btnEdit;

@property (weak, nonatomic) IBOutlet Button *btnUndo;

@property (weak, nonatomic) IBOutlet Button *btnSave;

 

@property (weak, nonatomic) IBOutlet UIButton *btnYellow;

@property (weak, nonatomic) IBOutlet UIButton *btnGreen;

@property (weak, nonatomic) IBOutlet UIButton *btnRed;

@property (weak, nonatomic) IBOutlet UIButton *btnBlack;

 

@property (weak, nonatomic) UIImage * imageForMainView;

 

@property (weak, nonatomic) Picture * pic;

 

 

@end

It just has the outlets for the buttons and the image property and the picture object property. (You can easely change the type Button to UIButton, it's only for layout purpose.

Now all you need is the implementation file:

//

//  PictureEditVC.m

//  LocationReport

//

//  Created by Mark Deraeve on 03/06/14.

//  Copyright (c) 2014 AssistU. All rights reserved.

//

 

#import "PictureEditVC.h"

#import <AssetsLibrary/AssetsLibrary.h>

 

@interfacePictureEditVC ()

 

@end

 

@implementation PictureEditVC

{

    bool drawActive;

    CGPoint cumTranslation;

    CGFloat red;

    CGFloat green;

    CGFloat blue;

    CGFloat brush;

    CGFloat opacity;

    CGRect pageRect;

    UIImage * initImage;

    UIImageView * tempDrawImage;

    UIImageView * mainImageView;

    UIScrollView * scrollView;

    bool mouseSwiped;

    CGPoint lastPoint;

    CGPoint scaleLastPoint;

}

 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    returnself;

}

 

- (void)viewDidLoad

{

    [superviewDidLoad];

     [GeneralFunctionsMakeSelectedButton:self.btnBlack];

}

 

-(void) viewDidAppear:(BOOL)animated

{

    // Do any additional setup after loading the view from its nib.

    tempDrawImage.userInteractionEnabled =YES;

    cumTranslation = CGPointMake(0,0);

    

    drawActive = NO;

    red = 0.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    

    brush = 3.0;

    opacity = 1.0;

    

    mainImageView.contentMode = UIViewContentModeScaleAspectFit;

    if (self.imageForMainView)

    {

        mainImageView = [[UIImageViewalloc] initWithImage:self.imageForMainView];

        pageRect = CGRectMake(0, 0, self.imageForMainView.size.width, self.imageForMainView.size.height);

        //mainImageView.image = self.imageForMainView;

    }

    else

    {

        pageRect = self.view.frame;

        pageRect.origin = CGPointMake(0,0);

        mainImageView = [[UIImageViewalloc] initWithFrame:pageRect];

    }

    

    tempDrawImage =[[UIImageViewalloc] initWithFrame:pageRect];

    

    scrollView = [[UIScrollViewalloc] initWithFrame:pageRect];

    scrollView.delegate = self;

    scrollView.minimumZoomScale = 0.3;

    scrollView.maximumZoomScale = 3.0;

    

    [scrollViewsetBackgroundColor:[UIColorredColor]];

    

    scrollView.contentSize= pageRect.size;

    

    [scrollViewaddSubview:mainImageView];

    [scrollViewaddSubview:tempDrawImage];

    [scrollViewsendSubviewToBack:mainImageView];

    

    [self.view addSubview:scrollView];

    [self.viewsendSubviewToBack: scrollView];

    

    UIPanGestureRecognizer *Scrolling  = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onScroll:)];

    UIPinchGestureRecognizer *Zooming   = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onZoom:)];

    UITapGestureRecognizer *Tap   = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];

    

    [scrollView addGestureRecognizer:Scrolling];

    [scrollView addGestureRecognizer:Zooming];

    [scrollView addGestureRecognizer:Tap];

}

 

#pragma mark Zoom / Draw

 

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

{

    return tempDrawImage;

}

 

- (void)onTap:(UIPinchGestureRecognizer*)sender

{

    if (drawActive)

    {

        UIGraphicsBeginImageContext(tempDrawImage.frame.size);

        [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

        

        CGPoint currentPoint = [sender locationInView:tempDrawImage];

        

        CGPoint scalePoint;

        scalePoint.x = currentPoint.x*scrollView.zoomScale;

        scalePoint.y = currentPoint.y*scrollView.zoomScale;

        

        CGRect borderRect = CGRectMake(scalePoint.x, scalePoint.y, brush, brush);

        CGContextRef context = UIGraphicsGetCurrentContext();

        CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);

        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);

        CGContextSetLineWidth(context, brush);

        CGContextFillEllipseInRect (context, borderRect);

        CGContextStrokeEllipseInRect(context, borderRect);

        CGContextFillPath(context);

        

        CGContextFlush(UIGraphicsGetCurrentContext());

        tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        

        //Draw dot.

        

    }

}

 

- (void)onZoom:(UIPinchGestureRecognizer*)sender

{

    if (!drawActive)

    {

        sender.view.transform = CGAffineTransformScale(sender.view.transform, sender.scale, sender.scale);

        sender.scale = 1;

    }

}

 

- (void)onScroll:(UIPanGestureRecognizer*)sender

{

    if (drawActive==NO)

    {

        CGPoint translation = [sender translationInView:self.view];

        sender.view.center = CGPointMake(sender.view.center.x + translation.x,

                                         sender.view.center.y + translation.y);

        [sender setTranslation:CGPointMake(0, 0) inView:self.view];

        cumTranslation = CGPointMake(cumTranslation.x + translation.x,

                                     cumTranslation.y + translation.y);

    }

    else

    {// Processing the drawing by using comparing:

        if (sender.state == UIGestureRecognizerStateBegan)

        { /* drawing began */

            

            mouseSwiped = NO;

            

            lastPoint = [sender locationInView:tempDrawImage];

            scaleLastPoint.x = lastPoint.x*scrollView.zoomScale;

            scaleLastPoint.y = lastPoint.y*scrollView.zoomScale;

            

        }

        else if (sender.state == UIGestureRecognizerStateChanged)

        { /* drawing occured */

            mouseSwiped = YES;

            

            CGPoint currentPoint = [sender locationInView:tempDrawImage];

            

            CGPoint scalePoint;

            scalePoint.x = currentPoint.x*scrollView.zoomScale;

            scalePoint.y = currentPoint.y*scrollView.zoomScale;

            

            UIGraphicsBeginImageContext(tempDrawImage.frame.size);

            [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

            CGContextMoveToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

            CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), scalePoint.x, scalePoint.y);

            CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

            CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );

            CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);

            CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);

            

            CGContextStrokePath(UIGraphicsGetCurrentContext());

            tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

            [tempDrawImage setAlpha:opacity];

            UIGraphicsEndImageContext();

            

            scaleLastPoint = scalePoint;

        }

        else if (sender.state == UIGestureRecognizerStateEnded)

        { /* drawing ended */

            if(!mouseSwiped) {

                UIGraphicsBeginImageContext(tempDrawImage.frame.size);

                [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

                CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

                CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);

                CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);

                CGContextMoveToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

                CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

                CGContextStrokePath(UIGraphicsGetCurrentContext());

                CGContextFlush(UIGraphicsGetCurrentContext());

                tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

                UIGraphicsEndImageContext();

            }

        }

    }

}

 

#pragma mark Button events

 

- (IBAction)btnEditClicked:(id)sender

{

    drawActive = !drawActive;

    self.btnBlack.hidden = !drawActive;

    self.btnGreen.hidden = !drawActive;

    self.btnRed.hidden = !drawActive;

    self.btnYellow.hidden = !drawActive;

    if (drawActive==YES)

    {

        [self.btnEdit setTitle:[[VariableStore sharedInstance] Translate:@"$PO$Zoom"] forState:UIControlStateNormal];

    }

    else

    {

        [self.btnEdit setTitle:[[VariableStore sharedInstance] Translate:@"$PO$Draw"] forState:UIControlStateNormal];

    }

}

- (IBAction)btnYellowClicked:(id)sender

{

    red = 255/255.0;

    green = 255.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnYellow];

}

- (IBAction)btnGreenClicked:(id)sender

{

    red = 0.0/255.0;

    green = 255.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnGreen];

}

- (IBAction)btnRedClicked:(id)sender

{

    red = 255.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnRed];

}

- (IBAction)btnBlackClicked:(id)sender

{

    red = 0.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnBlack];

}

 

-(void) SetClicked:(UIButton *) btn

{

    [GeneralFunctions MakeSimpleButton:self.btnBlack];

    [GeneralFunctions MakeSimpleButton:self.btnRed];

    [GeneralFunctions MakeSimpleButton:self.btnYellow];

    [GeneralFunctions MakeSimpleButton:self.btnGreen];

    [GeneralFunctions MakeSelectedButton:btn];

}

 

- (IBAction)btnUndoClicked:(id)sender

{

    tempDrawImage.image = nil;

}

- (IBAction)btnSaveClicked:(id)sender

{

    if (tempDrawImage!=nil)

    {

        UIGraphicsBeginImageContext(mainImageView.frame.size);

        

        [mainImageView.image drawInRect:CGRectMake(0, 0, mainImageView.frame.size.width, mainImageView.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];

        [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];

        mainImageView.image = UIGraphicsGetImageFromCurrentImageContext();

        tempDrawImage.image = nil;

        UIGraphicsEndImageContext();

 

        //overwrite the picture in the picture library

        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

        [library writeImageToSavedPhotosAlbum:mainImageView.image.CGImage orientation:(ALAssetOrientation)mainImageView.image.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error )

         {

             if (self.pic)

             {

                 self.pic.picture_url = [NSString stringWithFormat:@"%@", assetURL];

                 [DBStore SaveContext];

             }

         }];

        [self.navigationController popViewControllerAnimated:YES];

    }

}

 

- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

@end

 

Want to try this yourself, you could lose the Picture stuff, just provide the image to the view controller. Copy paste the code, with some easy fixes it should run perfectly. If needed, I could provide a demo project.


iOS: UITextField in UICollectionView get behind keyboard.

OK, I already posted a solution to move the view up when the keyboard shows so your textfield or view is not behind, but on top of the keyboard. 

Recently I had a similar problem, but this solution was not working exactly is I wanted. The problem was that when clicked in a textfield that was located in the top of the collection view, the field got moved out of screen. 

The first and easy part is moving the view up when the keyboard shows, just by implementing the solution found on all forums registering the keyboardwilshow and hide notifications:

- (void)viewWillAppear:(BOOL)animated

{

    // register for keyboard notifications

    [[NSNotificationCenterdefaultCenter] addObserver:self

                                             selector:@selector(keyboardWillShow)

                                                 name:UIKeyboardWillShowNotification

                                               object:nil];

    

    [[NSNotificationCenterdefaultCenter] addObserver:self

                                             selector:@selector(keyboardWillHide)

                                                 name:UIKeyboardWillHideNotification

                                               object:nil];

}

 

- (void)viewWillDisappear:(BOOL)animated

{

    // unregister for keyboard notifications while not visible.

    [[NSNotificationCenterdefaultCenter] removeObserver:self

                                                    name:UIKeyboardWillShowNotification

                                                  object:nil];

    

    [[NSNotificationCenterdefaultCenter] removeObserver:self

                                                    name:UIKeyboardWillHideNotification

                                                  object:nil];

 

}

These 2 selectors simply move the view up or down, I made the move up function general:

//method to move the view up/down whenever the keyboard is shown/dismissed

+(void)setViewMovedUp:(BOOL)movedUp AndView:(UIView *) vw

{

    int movementDistance = 80; // tweak as needed

    if ( UIInterfaceOrientationIsLandscape([[UIApplicationsharedApplication]statusBarOrientation]))

    {

        movementDistance = kOFFSET_FOR_KEYBOARDHOR;

    }

    else

    {

        movementDistance = kOFFSET_FOR_KEYBOARDVER;

    }

    const float movementDuration = 0.3f; // tweak as needed

    

    int movement = (movedUp ? -movementDistance : movementDistance);

    

    [UIViewbeginAnimations: @"anim"context: nil];

    [UIViewsetAnimationBeginsFromCurrentState: YES];

    [UIView setAnimationDuration: movementDuration];

    vw.frame = CGRectOffset(vw.frame, 0, movement);

    [UIViewcommitAnimations];

 

}

So this function is called when the keyboard moves up or down like this:

-(void)keyboardWillShow

{

    [GeneralFunctionssetViewMovedUp:YES AndView:self.view];

 

}

 

-(void)keyboardWillHide

{

    [GeneralFunctionssetViewMovedUp:NOAndView:self.view];

 

}

This already works, but will shift the top textfields out of screen:

 

When clicked on one of the top fields, you can see the field moving out of screen, I clicked in the textfield with text menu, it's out of screen after the keyboard shows:

Now how to solve this. I had to find out when clicked in the collection view in a textfield that would appear behind the keyboard if shown.

To solve this I made a delegate function in the cell view controller that would be called when the textfield edit did begin. 

-(void) collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath

{

    selectedIndexPath = indexPath;

}

 

-(void) EditDidBegin:(UICollectionViewCell *)cell

{

    NSIndexPath * path = [self.colTranslations indexPathForCell:cell];

    [self SetMove:path];

}

 

-(void) SetMove:(NSIndexPath *) indexPath

{

    UICollectionViewLayoutAttributes *attributes = [self.colTranslations layoutAttributesForItemAtIndexPath:indexPath];

    

    CGRect cellRect = attributes.frame;

    

    CGRect cellFrameInSuperview = [self.colTranslations convertRect:cellRect toView:[self.colTranslations superview]];

    

    NSLog(@"%f",cellFrameInSuperview.origin.y);

    if (cellFrameInSuperview.origin.y > kOFFSET_FOR_KEYBOARDHOR)

    {

        moveScreen=YES;

    }

    else

    {

        moveScreen=NO;

    }

 

}

 

So the function SetMove will calculate where clicked in the superview and chooses if the keyboard needs to be shifted up or not.

Just add these checks to the selectors of the keyboard will show and hide notifications and it will work:

-(void)keyboardWillShow

{

    if (moveScreen==YES)

    {

        [GeneralFunctionssetViewMovedUp:YESAndView:self.view];

        up=YES;

    }

    else

    {

        up=NO;

    }

}

 

-(void)keyboardWillHide

{

    if (moveScreen==YES && up==YES)

    {

        [GeneralFunctionssetViewMovedUp:NOAndView:self.view];

    }

 

}

Now do not forget to declare the bool 

bool moveScreen;

 

bool up;

 
And the result is this:

You see the Menu field that was clicked is still visible.

Now if I click in a field that is behind the keyboard:

The screen moves up!

If you hide the keyboard, the screen will get back in its original position.


Using LINQ to concatinate an object property from a list of objects

Just put this in a little demo to make it clear. I created a simple object Person, with ID, FirstName and LastName.

Now I fill a List with 3 persons (later I'll fill it with 20000 to do a performance check):

List<Person> persons = newList<Person>();

{

    newPerson(){ID=0, FirstName="Mark", LastName="Deraeve"},

    newPerson(){ID=1, FirstName="Priscilla", LastName="Lauwerijssen"},

    newPerson(){ID=2, FirstName="Anne-Lisa", LastName="Deraeve"}

};

Instead of looping over the list and concatinating it, like this:

start = DateTime.Now;

foreach (Person p in persons)

{

    sPersons = String.Format("{0},{1}", sPersons, p.FirstName);

}

//remove front ,

Console.WriteLine("With Loop: " + sPersons);

I can also just use 1 line to accomplisch this:

Console.WriteLine("With LINQ: " +string.Join(",", persons.Select(p => p.FirstName)));

Result:

 

Now let's say you do not want all records, this makes it even more easy, just put a Where before the Select and you got it!

Let's only show person named 'Deraeve':

Console.WriteLine("With LINQ: " +string.Join(",", persons.Where(p => p.LastName == "Deraeve").Select(p => p.FirstName)));

Result:

 

Now last, lets try to see performance difference. I'll fill the list with 20000 persons. This is where the power comes in. (The number 188889 or 188890(+1 for the extra comma) is the length of the result string)

 

Amazingly, the LINQ only uses 3 or 4 milliseconds, the old for loop takes 1-3 (There is a difference when using String.Format (slower) or just "string + ','+ "string") seconds. This can really make a big difference in today's apps.

So here the complete code so you can test it yourself:

Code of the console app:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace TestConsoleApp

{

    classProgram

    {

        staticvoid Main(string[] args)

        {

            List<Person> persons = newList<Person>();

            /*{

                new Person(){ID=0, FirstName="Mark", LastName="Deraeve"},

                new Person(){ID=1, FirstName="Priscilla", LastName="Lauwerijssen"},

                new Person(){ID=2, FirstName="Anne-Lisa", LastName="Deraeve"}

            };*/

 

            for (int i = 0; i < 20000; i++)

            {

                persons.Add(newPerson() { ID = i, FirstName = string.Format("Name{0}", i), LastName = "Deraeve" });

            }

 

            string sPersons="";

            DateTime start = DateTime.Now;

            TimeSpan ts;

 

            start = DateTime.Now;

            sPersons = string.Join(",", persons.Select(p => p.FirstName));

            ts = DateTime.Now.Subtract(start);

            Console.WriteLine("With LINQ: " + sPersons.Length);

            Console.WriteLine(String.Format("Seconds:{0} Miliseconds{1} ", ts.Seconds, ts.Milliseconds));

            Console.WriteLine("");

 

            sPersons = "";

 

            start = DateTime.Now;

            sPersons = string.Join(",", persons.Where(p => p.LastName == "Deraeve").Select(p => p.FirstName));

            ts = DateTime.Now.Subtract(start);

            Console.WriteLine("With LINQ and Where: " + sPersons.Length);

            Console.WriteLine(String.Format("Seconds:{0} Miliseconds{1} ", ts.Seconds, ts.Milliseconds));

            Console.WriteLine("");

 

            sPersons="";

 

            start = DateTime.Now;

            foreach (Person p in persons)

            {

                sPersons = String.Format("{0},{1}", sPersons, p.FirstName);

            }

            ts = DateTime.Now.Subtract(start);

 

            Console.WriteLine("With for loop: " + sPersons.Length);

            Console.WriteLine(String.Format("Seconds:{0} Miliseconds{1} ",ts.Seconds,ts.Milliseconds));

            Console.WriteLine("");

 

            sPersons = "";

 

            start = DateTime.Now;

            foreach (Person p in persons)

            {

                if (p.LastName == "Deraeve")

                {

                    sPersons = String.Format("{0},{1}", sPersons, p.FirstName);

                }

            }

 

            ts = DateTime.Now.Subtract(start);

 

            Console.WriteLine("With for loop and if: " + sPersons.Length);

            Console.WriteLine(String.Format("Seconds:{0} Miliseconds{1} ", ts.Seconds, ts.Milliseconds));

            Console.WriteLine("");

 

            sPersons = "";

 

            start = DateTime.Now;

            foreach (Person p in persons)

            {

                sPersons = sPersons + ',' + p.FirstName;

            }

            ts = DateTime.Now.Subtract(start);

 

            Console.WriteLine("With for loop without format: " + sPersons.Length);

            Console.WriteLine(String.Format("Seconds:{0} Miliseconds{1} ", ts.Seconds, ts.Milliseconds));

            Console.WriteLine("");

 

            Console.ReadKey();

        }

    }

}

Code of the Person class:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace TestConsoleApp

{

    publicclassPerson

    {

        publicstring FirstName { get; set; }

 

        publicstring LastName { get; set; }

 

        publicint ID { get; set; }

 

    }

}

Things even get better for the Join statement when we add the last name to the result:

sPersons = string.Join(",", persons.Where(p => p.LastName == "Deraeve").Select(p => p.FirstName + ' ' + p.LastName));

VS

foreach (Person p in persons)

{

    sPersons = String.Format("{0},{1} {2}", sPersons, p.FirstName, p.LastName);

}


Result: 



First app in the App Store !!!

Yesterday the Wiezer app got approved in the app store. For now only in Dutch, but I plan to release a multi language (English en Dutch) app with some improvements soon.

Here the first review of the app looking good! Check in AppStore!

First app is a special feeling, next would be a second app that could reach and interest the whole world.


Wiezer

Today I posted my first app in the app store. The process was relative easy. Now just wait until the app is reviewed. The manual on the small cards game assistant app can be found here!

 


iOS: UITextView gets behind the keyboard

Very annoying problem when you make a simple view with on the bottom a UITextView. Reading a lot of solutions involving using a UIScrollview, I kept looking for an easy solution. And I found it.

The solution involves implementing the UITextView's delegate and moving up or down the view when the editing starts or ends.

Because the UITextView view doesn't have sent events like the UITextField, we need to implement the UITextViewDelegate .

Do not forget to set the delegate outlet for the TextView.

 

The rest is almost as easy:

#define kOFFSET_FOR_KEYBOARD 160.0

 

 

-(void) textViewDidBeginEditing:(UITextView *)textView

{

    if ([textView isEqual:self.txtRemark])

    {

        //move the main view, so that the keyboard does not hide it.

        if  (self.view.frame.origin.y >= 0)

        {

            [selfsetViewMovedUp:YES];

        }

    }

}

 

-(void) textViewDidEndEditing:(UITextView *)textView

{

    if ([textView isEqual:self.txtRemark])

    {

        if (self.view.frame.origin.y < 0)

        {

            [self setViewMovedUp:NO];

        }

    }

}

//method to move the view up/down whenever the keyboard is shown/dismissed

-(void)setViewMovedUp:(BOOL)movedUp

{

    [UIViewbeginAnimations:nilcontext:NULL];

    [UIViewsetAnimationDuration:0.3]; // if you want to slide up the view

    

    CGRect rect = self.view.frame;

    if (movedUp)

    {

        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard

        // 2. increase the size of the view so that the area behind the keyboard is covered up.

        rect.origin.y -= kOFFSET_FOR_KEYBOARD;

        rect.size.height += kOFFSET_FOR_KEYBOARD;

    }

    else

    {

        // revert back to the normal state.

        rect.origin.y += kOFFSET_FOR_KEYBOARD;

        rect.size.height -= kOFFSET_FOR_KEYBOARD;

    }

    self.view.frame = rect;

    

    [UIViewcommitAnimations];

}

 

We will edit the field remark:

After clicking in the field, the screen moves up:

When finished (by clicking the hide keyboard sign, or by editing another field, the screen moves down again.


SAP: Adobe forms not printing the quantity set in VV32

When printing PDF invoices or any other document in SAP, you need to give the correct output parameters.

My problem was that when I specified 3 as quantity (ANZAHL) as the number of copies to print. But everytime only 1 appeared.

At the end, the problem was solved by settings the number of copies to the NAST-ANZAHL parameter:

fp_outputparams-copies    = nast-anzal.

So the complete set of parameters looks like this:

IF gv_e_invoice EQ 'X'.

    fp_outputparams-preview       = 'X'.

    fp_outputparams-nodialog      = gv_e_invoice.

 

  ELSE.

    fp_outputparams-reqimm        = 'X'.

    fp_outputparams-preview       = 'X'.

    fp_outputparams-nodialog      = 'X'.

    fp_outputparams-copies    = nast-anzal.

  ENDIF.

 

  fp_outputparams-covtitle      = 'Invoice'.

  fp_outputparams-reqnew        = 'X'.

  fp_outputparams-reqfinal      = 'X'.

 

  IF NOT nast-ldest IS INITIAL.

    fp_outputparams-dest = nast-ldest.

  ELSE.

    fp_outputparams-dest          = 'LP01'.

  ENDIF.

 

  fp_outputparams-getpdf        = gv_e_invoice.

I have a difference when generating PDF for email. On our development system this did the trick.

But then on the QAS system it still ony printed 1 time. The solution to this couldn't be in the code, for it worked on development. It appeared to be a printer setting in SAP that wasn't correct. I had to set the Host Spool Access Method to C: Direct operating system call. 

 


SAP: Program (IS_VARIANT-REPORT) not specified (SAP Message 0K522)

I looked on the internet for a while, but couldn't find a solution that worked for me.
I noticed that when I used the transaction, this error came, but when I ran the program from SE383 it didn't gave any error.

So I looked myself for a short while and found out that it was the transaction I created that had a problem. I recreated the transaction using SE80. First I deleted the old transaction. Then I created the transaction following these steps:

1. Create the tranaction:

2. Select the second option from the list:

3. Then fill in the program behind the transaction and save.



I believe the mistake I did was the first time I created the transaction, I selected the first option in step 2.

SAP: Adobe Forms - Print Sales conditions on the back.

There are many solutions to find for something you would expect to be kind of default functionality. But no, no default, rahter painfull when you are figuring out you selve. In my opinion, the setup is not logic. If you try the logic way, you'll get strange and unexpected behaviour.  

So the one solution that works for me in all cases is still the by far simplest solution I found here!

Start with putting your pageset to Print on both sides:

 

To use this solution, you'll need 2 extra pages (I called them BackPage and Dummy). 

Then you set the first page (the page that contains your data) like this:

Next you set the backpage like this:

And last the last page (Dummy) like this:

This will make sure that any blank page is set to the sales conditions. This page doesn't have a content area, only the text of the salesconditions:

 

Now finally very important is to tell the content to where is has to go when the page is full. It must go to the content area of the FirstPage, not the content area of the back page. If we leave the field blank, it will automatically put the items on the back page.

 

 -->  --> 

Now the pages will come out nicely  with the sales conditions on the back, when there's only 1 page or more pages.

Just as example how you can design your sales conditions:

 

And the result:


iOS: Make a simple iOS7 ready button by subclassing UIButton

Very simple solution, but works like a charm.

Create a subclass of UIButton. 

There we overload the initFrame and important also the awakeFromNib:

- (id)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

        // Initialization code

        [GeneralFunctionsMakeNormalButton:self];

    }

    returnself;

}

 

-(void) awakeFromNib

{

    [GeneralFunctionsMakeNormalButton:self];

 

}

 
This general function looks like this:

+ (void) MakeNormalButton:(UIView *) vw

{

    vw.backgroundColor= [UIColorwhiteColor];

    [vw.layersetCornerRadius:10.0f];

    [vw.layersetBorderColor:[UIColorblueColor].CGColor];

    [vw.layersetBorderWidth:1.0f];

    [vw.layersetShadowColor:[UIColorgrayColor].CGColor];

    [vw.layersetShadowOpacity:0.5];

    [vw.layersetShadowRadius:2.0];

    [vw.layersetShadowOffset:CGSizeMake(2.0f, 2.0f)];

}

Now we also overwrite the setHighlighted method to show the button was selected:

-(void) setHighlighted:(BOOL)highlighted

{

    if (highlighted)

    {

        [selfhighLightView];

    }

    else

    {

        [selfcleanHighLightView];

    }

    [super setHighlighted:highlighted];

 

}

 

-(void) highLightView

{

    self.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);

    self.layer.shadowOpacity = 0.25;

    self.backgroundColor= [UIColorblueColor];

}

 

-(void) cleanHighLightView

{

    self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);

    self.layer.shadowOpacity = 0.5;

    self.backgroundColor= [UIColorwhiteColor];

}

 

Now you can change the class of your button in the storyboard designer to this class and your buttons get the new layout, no more programming todo.

 

Then from button less iOS 7 and button richt iOS 6 you get a uniform layout with very few changes. In the examples below, you'll see the iOS 6 layout on top, iOS 7 on the bottom.