Ancient Programming

What I encounter in my software part of life is in danger of being commented upon here

Version 2.0.0 of the IOSLinkedInAPI is available

Posted by Jacob von Eyben on January 30th, 2014

Just want people to know that they can grab the latest and greatest version (2.0.0) of the IOSLinkedInAPI

It is available as a cocoapod as well.

Changes:

  • Updated to use AFNetworking 2.x
  • persisting tokens between requests in NSUserDefaults
  • better/nicer presentation of authentication window on IPad
  • General bugfixing

Posted in ios, objective-c | 1 Comment »

IOS LinkedIn integration using OAuth2.

Posted by Jacob von Eyben on June 9th, 2013

I have created an opensource IOS library which can access the LinkedIn API using OAuth2.

The library is accessible at github: https://github.com/jeyben/IOSLinkedInAPI and the latest version 0.0.4 has been submitted to cocoapods central repository: http://cocoapods.org/?q=IOSLinkedInAPI

If you need to use LinkedIn in your IOS application, I hope you can use this library.

Enjoy!

Posted in ios, ipad, iphone, objective-c | 5 Comments »

Animating the drag drop example

Posted by Jacob von Eyben on April 9th, 2012

As an update to my original ios drag drop post I have added an animation to the snap back to original position.

The changes is made to the DragContext.m file where the snapToOrignalPostion is changed to look like the following:

- (void)snapToOriginalPosition {
    [UIView animateWithDuration:0.3 animations:^() {
        CGPoint originalPointInSuperView = [_draggedView.superview convertPoint:_originalPosition fromView:_originalView];
        _draggedView.frame = CGRectMake(originalPointInSuperView.x, originalPointInSuperView.y, _draggedView.frame.size.width, _draggedView.frame.size.height);
    } completion:^(BOOL finished) {
        _draggedView.frame = CGRectMake(_originalPosition.x, _originalPosition.y, _draggedView.frame.size.width, _draggedView.frame.size.height);
        [_draggedView removeFromSuperview];
        [_originalView addSubview:_draggedView];
    }];
}

First we animate the dragged object back to the original position (in the super view containing all drag objects and drop areas). When the animation is completed, we change the superview.

Posted in animation, how to, ios, ipad, iphone, mac, objective-c | 1 Comment »

Drag and drop between multiple UIViews in iOS

Posted by Jacob von Eyben on April 5th, 2012

This is an example showing how to drag and and drop UIViews between other UIViews in iOS.

Illustrates the drag and drop UIView stack

Illustrates the drag and drop UIView stack

The example is broken into the following four and a half step.

Example broken into steps
1. register a UIPanGestureRecognizer to be able to get drag panning events  (in our case drag events).
2. detect if the panning started above the view we would like to drag.
3. move the view according to the panning - which means removing the object from the view we would like to drag it from.
4. when dragging ends, drop the view onto the view below the dragged object.
4.a. optionally reset the dragged object, if the object is dragged onto a view which we don’t consider as a valid drop view.


In my example I have created a more or less generic DragDropManager, which is responsible for actually handling the UIViews objects we allow to drag ‘n drop and the UIVeiws we allow to drop onto.

Step 1
The manager registers the draggable objects and drop areas and takes care of detecting when a object should be dragged and dropped. This is done by receiving pan events from the UIPanGestureRecognizer. I recommend registering the gesture recognizer to a UIView having a frame covering the entire drag and drop area, because if you bind the recognizer to the actual draggable objects, you will loose the pan sequence as soon as you change super view for the dragged object (and changing super view is the hole point of dragging objects between UIViews).

@implementation DragDropBetweenViewsViewController
...
- (void)viewDidLoad {
    [super viewDidLoad];
    _viewA = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];
    [_viewA setBackgroundColor:[UIColor greenColor]];
    _viewA.tag = 1;
    _viewB = [[UIView alloc] initWithFrame:CGRectMake(0, 220, 320, 200)];
    [_viewB setBackgroundColor:[UIColor yellowColor]];
    _viewB.tag = 2;

    [[self view] addSubview:_viewA];
    [[self view] addSubview:_viewB];
    //[[self view] addSubview:_viewB];

    //add elements to drag and drop
    UIView *dragDropView1 = [[[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)] autorelease];
    UIView *dragDropView2 = [[[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)] autorelease];
    [_viewA addSubview:dragDropView1];
    [_viewB addSubview:dragDropView2];
    [dragDropView1 setBackgroundColor:[UIColor redColor]];
    [dragDropView2 setBackgroundColor:[UIColor blueColor]];
    NSMutableArray *draggableSubjects = [[NSMutableArray alloc] initWithObjects:dragDropView1, dragDropView2, nil];
    NSMutableArray *droppableAreas = [[NSMutableArray alloc] initWithObjects:_viewA, _viewB, nil];
    _dragDropManager = [[DragDropManager alloc] initWithDragSubjects:draggableSubjects andDropAreas:droppableAreas];
    [draggableSubjects release];
    [droppableAreas release];

    UIPanGestureRecognizer * uiTapGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:_dragDropManager action:@selector(dragging:)];
    [[self view] addGestureRecognizer:uiTapGestureRecognizer];
    [uiTapGestureRecognizer release];
}
...
@end

Step 2
The manager switches on the different states of the UIPanGestureRecognizer and detects if a panning  starts on one of our dragSubjects. If so we store the DragContext containing the dragged view and the starting point (to be able to snap back to the offset in case the drag ends outside a drop zone). At the same time we change superview for the dragged object, by attaching it to the UIView spanning the entire area. This is done to ensure our UIView is shown above all other subviews and hence is visible during the entire drag drop session.

Step 3
During the drag process we simply move the object according to the coordinates of the UIGestureRecognizer.

Step 4
When the panning ends (the UIGestureRecognizer is in state UIGestureRecognizerStateEnded), we check to see if the dropped view is above any registered drop areas. If so, we add the dragged UIView as a subview of the detected drop area.

Step 4.a.
If we don’t recognize a valid drop area beneath our dragged view, we snap the dragged UIView back to its original superview and position.


The DragManager code can be seen here:

@implementation DragDropManager {

    NSArray *_dragSubjects;
    NSArray *_dropAreas;
    DragContext *_dragContext;
}

@synthesize dragContext = _dragContext;
@synthesize dropAreas = _dropAreas;

- (id)initWithDragSubjects:(NSArray *)dragSubjects andDropAreas:(NSArray *)dropAreas {
    self = [super init];
    if (self) {
        _dropAreas = [dropAreas retain];
        _dragSubjects = [dragSubjects retain];
        _dragContext = nil;
    }

    return self;
}

- (void)dealloc {
    [_dragSubjects release];
    [_dragContext release];
    [_dropAreas release];
    [super dealloc];
}

- (void)dragObjectAccordingToGesture:(UIPanGestureRecognizer *)recognizer {
    if (self.dragContext) {
        CGPoint pointOnView = [recognizer locationInView:recognizer.view];
        self.dragContext.draggedView.center = pointOnView;
    }
}

- (void)dragging:(id)sender {
    UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *) sender;
    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan: {
            for (UIView *dragSubject in _dragSubjects) {
                CGPoint pointInSubjectsView = [recognizer locationInView:dragSubject];
                BOOL pointInSideDraggableObject = [dragSubject pointInside:pointInSubjectsView withEvent:nil];
                NSLog(@"point%@ %@ subject%@", NSStringFromCGPoint(pointInSubjectsView), pointInSideDraggableObject ? @"inside" : @"outside", NSStringFromCGRect(dragSubject.frame));
                if (pointInSideDraggableObject) {
                    NSLog(@"started dragging an object");
                    self.dragContext = [[[DragContext alloc] initWithDraggedView:dragSubject] autorelease];
                    [dragSubject removeFromSuperview];
                    [recognizer.view addSubview:dragSubject];
                    [self dragObjectAccordingToGesture:recognizer];
                } else {
                    NSLog(@"started drag outside drag subjects");
                }
            }
            break;
        }
        case UIGestureRecognizerStateChanged: {
            [self dragObjectAccordingToGesture:recognizer];
            break;
        }
        case UIGestureRecognizerStateEnded: {
            if (self.dragContext) {
                UIView *viewBeingDragged = self.dragContext.draggedView;
                NSLog(@"ended drag event");
                CGPoint centerOfDraggedView = viewBeingDragged.center;
                BOOL droppedViewInKnownArea = NO;
                for (UIView *dropArea in self.dropAreas) {
                    CGPoint pointInDropView = [recognizer locationInView:dropArea];
                    NSLog(@"tag %li pointInDropView %@ center of dragged view %@", dropArea.tag, NSStringFromCGPoint(pointInDropView), NSStringFromCGPoint(centerOfDraggedView));
                    if ([dropArea pointInside:pointInDropView withEvent:nil]) {
                        droppedViewInKnownArea = YES;
                        NSLog(@"dropped subject %@ on to view tag %li", NSStringFromCGRect(viewBeingDragged.frame), dropArea.tag);
                        [viewBeingDragged removeFromSuperview];
                        [dropArea addSubview:viewBeingDragged];
                        //change origin to match offset on new super view
                        viewBeingDragged.frame = CGRectMake(pointInDropView.x - (viewBeingDragged.frame.size.width / 2), pointInDropView.y - (viewBeingDragged.frame.size.height / 2), viewBeingDragged.frame.size.width, viewBeingDragged.frame.size.height);
                    }
                }

                if (!droppedViewInKnownArea) {
                    NSLog(@"release draggable object outside target views - snapping back to last known location");
                    [self.dragContext snapToOriginalPosition];
                }

                self.dragContext = nil;
            } else {
                NSLog(@"Nothing was being dragged");
            }
            break;
        }
    }
}
@end

The DragContext containing the dragged view and the original position and view

#import "DragContext.h"

@implementation DragContext {

    UIView *_draggedView;
    CGPoint _originalPosition;
    UIView *_originalView;
}
@synthesize draggedView = _draggedView;

- (id)initWithDraggedView:(UIView *)draggedView {
    self = [super init];
    if (self) {
        _draggedView = [draggedView retain];
        _originalPosition = _draggedView.frame.origin;
        _originalView = [_draggedView.superview retain];
    }

    return self;
}

- (void)dealloc {
    [_draggedView release];
    [_originalView release];
    [super dealloc];
}

- (void)snapToOriginalPosition {
    [_draggedView removeFromSuperview];
    [_originalView addSubview:_draggedView];
    _draggedView.frame = CGRectMake(_originalPosition.x, _originalPosition.y, _draggedView.frame.size.width, _draggedView.frame.size.height);
}
@end

Room for improvements
- animations should be added to show more smooth look and feel.. This blog post demonstrates how to add animations to the snap back to orignial position.
- Consider the visible order of the views when selecting draggable UIView, if multiple draggable UIViews is stacked upon each other.

Posted in how to, ios, ipad, iphone, objective-c | 18 Comments »

How to implement drop shadow and immerse effects for UIViews

Posted by Jacob von Eyben on December 30th, 2011


It is pretty easy to create a drop shadow or immerse effect like this in iOS.

Here is two static helper methods I created to do the trick.
The first method is capable of dropping a shadow from one view onto another:

+ (void)dropColor:(UIColor *)dropColor from:(UIView *)dropFromView onto:(UIView *)toView x:(CGFloat)x y:(CGFloat)y {
    CGFloat cornerRadius = [[dropFromView layer] cornerRadius];
    UIView *shadow = [[[UIView alloc] initWithFrame:dropFromView.frame] autorelease];
    [[shadow layer] setCornerRadius:cornerRadius];
    CGRect dropRect = dropFromView.frame;
    shadow.frame = CGRectMake(dropRect.origin.x + x, dropRect.origin.y + y, dropRect.size.width, dropRect.size.height);
    [shadow setBackgroundColor:dropColor];
    [toView insertSubview:shadow belowSubview:dropFromView];
}

The second takes advantage of the first and creates an effect that makes it looks like the first view is immersed into the other:

+ (void)immerse:(UIView *)immerseView into:(UIView *)toView depth:(CGFloat)depth {
    CGFloat x = 0.7;
    CGFloat y = 1.4;
    [UIUtil dropColor:[UIColor darkGrayColor] from:immerseView onto:toView x:-1 * x * depth y:-1 * y * depth];
    [UIUtil dropColor:[UIColor whiteColor] from:immerseView onto:toView x:x*depth y:y*depth];
}

The following effect can be added to a tableview by doing this:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[_tableView layer] setCornerRadius:5];
    [UIUtil immerse:_tableView into:self.view depth:1.5];
}

Posted in ios, iphone, mac, objective-c | No Comments »

Extend UITabBarController with an arrow marker

Posted by Jacob von Eyben on September 18th, 2011

I have seen a couple of iPhone apps using an UITabBarController having an arrow showing just above the selected UITabBarItem as an extra visual pointer to the current visible view. At the same time the arrow moves animated between the items as the user selects them.

I decided to try implement the same effect and I have shared my solution here.

The solution is made generic enough to handle any number of UITabBarItems in the UITabBarController and basically what it does is:

  • In the viewDidAppear selector, the arrow is places above the first element. This is done by calculating the size and location (also known as the CGRect) of the first UITabBarItem. Then a new UIImageView is created and positioned centered and above the UITabBarItem. Again some math is used to find the offset to use for the newly created UIImageView
  • In the didSelectItem a transition is used to animate the movement of the arrow between the items as they are selected.

My CustomUITabBarController.h and CustomUITabBarController.m files looks like this:

@interface CustomUITabBarController : UITabBarController {
@private
    UIImageView *_imageView;
}
@end

The actual implementation looks like follows:

@interface CustomUITabBarController ()
@property(nonatomic, retain) UIImageView *imageView;
@end

@implementation CustomUITabBarController

@synthesize imageView = _imageView;

- (CGRect) rectForItem:(UITabBarItem *)item {
    NSUInteger itemsInTabBar = [self.tabBar.items count];

    CGSize itemSize = CGSizeMake(self.tabBar.frame.size.width / itemsInTabBar, self.tabBar.frame.size.height);
    //find current selected item index
    int currentIndexSelected = 0;
    //if the item is nil, we keep the index at zero (selects the first element)
    if (item != nil) {
        for (int i = 0; i < itemsInTabBar; i++) {
            UITabBarItem *currentItem = [self.tabBar.items objectAtIndex:(NSUInteger) i];
            if (currentItem == item) {
                currentIndexSelected = i;
            }
        }
    }
    //construct the rect that defines the current selected item
    return CGRectMake(lrint(currentIndexSelected * itemSize.width), itemSize.height, itemSize.width, itemSize.height);
}

- (CGRect)getRectForImage:(UIImage *)markerImage andTabPosition:(CGRect)itemRect {
    CGFloat windowHeight = [UIScreen mainScreen].bounds.size.height;
    CGRect markerImageRect = CGRectMake(itemRect.origin.x + lrint(itemRect.size.width / 2) - lrint(markerImage.size.width / 2), windowHeight - itemRect.size.height - markerImage.size.height, markerImage.size.width, markerImage.size.height);
    return markerImageRect;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];  //To change the template use AppCode | Preferences | File Templates.
    UIImage * markerImage = [UIImage imageNamed:@"tabMarker.png"];
    CGRect firstItemRect = [self rectForItem:nil];
    CGRect markerImageRect = [self getRectForImage:markerImage andTabPosition:firstItemRect];
    _imageView = [[UIImageView alloc] initWithFrame:markerImageRect];
    _imageView.image = markerImage;
    [self.view addSubview:_imageView];
}

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
    CGRect selectedItemRect = [self rectForItem:item];

    [UIView beginAnimations:@"move tabMarker" context:nil];
    [UIView setAnimationDuration:0.3f];
    _imageView.transform = CGAffineTransformMakeTranslation(selectedItemRect.origin.x,0);
    [UIView commitAnimations];
}
...
@end

Posted in how to, ios, iphone, mac, objective-c | No Comments »

Logging sql statements and parameters using hibernate

Posted by Jacob von Eyben on November 12th, 2009

If you need to log sql statements using hibernate you can turn debug on for the org.hibernate.sql logger.
That will log all sql statement, but the actual parameter values will not be logged.

To log the values bound to the hibernate prepared statements, you can turn on trace for the org.hibernate.type logger.

Unfortunately (apparently because of perfomance) the value of the org.hibernate.type logger is evaulated once in a static block, and cached, see: EnumType.java.

That prevents you from turning the log on and off during runtime using a simple jsp page like the log4jAdmin.jsp. Note that if it were possible you still had to add trace to the page as debug is currently the lowest value.

Of course there must be a reason why the hibernate implementation are caching the value of the org.hibernate.type logger, but I believe it is still valuable to be able to turn on sql logging with parameters at runtime.

For that purpose I came across the logDriver. A simple database driver written by Ryan Bloom, that is wrapping an existing jdbc driver.

To use the logdriver all you have to do is set the driver and connection url as follows:

driver:net.rkbloom.logdriver.LogDriver
url:jdbc:log_real_driver_class:real_jdbc_connection_url

Now you can turn on logging by setting the net.rkbloom.logdriver logger to debug.

Posted in how to, java | 1 Comment »

Any good maven support for Google Appengine?

Posted by Jacob von Eyben on June 5th, 2009

Yesterday I created a appengine project testing the JPA/Spring/Wicket stack.

This was created using the build.xml file found in the appengine documentation. It all worked fine, but I would like to build my project using maven so I started looking for a good maven plugin.

I found this blog talking about what features a good plugin should contain and a guy from the kindleit company claiming that they actually had implemented a maven-gae-plugin capable of executing most of these tasks.

Unfortunately I ran into several problems using this plugin, hence I have created an issue.

Untill these tasks in the issue has been fixed I would like to use another plugin.
Can anybody guide me in the direction of a more stable plugin?

Posted in java, maven | 1 Comment »

Howto setup smooth ssh access

Posted by Jacob von Eyben on October 24th, 2008

Everytime I have a new machine or a new ssh access to setup I forgot how to configure the access so I don’t have to type the username each time (if I login with a different user).
At the same time I would like to use a private rsa key. So here goes a note to myself and other scatter brains :-)

Instead of always have to type username and password like this:

[jeyben@machine ~]$ ssh username@www.ancientprogramming.com
Password:
Last login: Fri Oct 24 16:31:03 2008 from somewhere
[username@servername ~]$

I would like to just do the following:

[jeyben@machine ~]$ ssh ancientprogramming
Last login: Fri Oct 24 16:31:03 2008 from somewhere
[username@servername ~]$

Create and use a public/private key pair

Client

$ ssh-keygen -t rsa
$ scp ~/.ssh/id_rsa.pub www.ancientprogramming.com:~

Server

$ cat ~/id_rsa.pub >> .ssh/authorized_keys

Modify .ssh/config

Host ancientprogramming
  User <username>
  Port 22
  HostName www.ancientprogramming.com
  LocalForward 3307 localhost:3306 (setup a localforward for the default mysql port)

Posted in how to, ssh | No Comments »

Fixedformat4j now supports primitive datatypes as well as innerclasses

Posted by Jacob von Eyben on October 16th, 2008

Support for primitive datatypes is added to the latest 1.2.1 release of fixedformat4j. I would like to thank Marcos Lois Bermúdez for contributing with this extension. The latest release can be downloaded here.

The same 1.2.1 release also fixed the bug that made it impossible to use annotate and format static nested classes and inner classes

The complete changelist can be found on the project website.

In a few days the release should be available from ibiblio.

I encourage all users of fixedformat4j to contribute or create issues if they find a bug or would like new features to be added.

Posted in fixedformat4j, java | No Comments »