App development

UIViewController offsets scrollview subviews

I was just wondering why my Scrollview Content would always beoffset to what i set it up in Storyboard (Xcode). I use a UIViewController and changed it’s view’s class to UIScrollView.

The layout of my scrollview in Xcode Storyboard
The layout of my scrollview in Xcode Storyboard

The problem was, that all the content which i played out in Xcode was offset by the height of the toolbar during runtime. This did not happen when the view was of class UIView, as by default. I therefore tried to set the subviews frames during runtime but this seems to be restricted by the loaded nibs somehow.

Subviews have a offset in y direction with a height of the toolbar
Subviews have a offset in y direction with a height of the toolbar

for(UIView* subview in self.view.subviews){ [subview setFrame:CGRectOffset(subview.frame, 0, -[self navigationController].toolbar.frame.size.height)];}

I finally found the reason for my problem in the attributes inspector of my ViewController.

Turn "Adjust Scrollview Insets off"
Turn “Adjust Scrollview Insets off”

The Option “Adjust Scroll View Insets” must be disabled for this purpose. It’s probably something which tries to prevent the offset from being hidden by the toolbar. But using the layout in interface builder to manage your subviews obviously interferes with this option. You should therefore turn it off!

offset removed by turning off "Adjust Scrollview Insets"
offset removed by turning off “Adjust Scrollview Insets”

Cheers,

Jan

App development

Ratelet a OSX Menulet to control iTunes’ rating and volume

Hey, i finally submitted my first official App to the Apple App Store! Check it out 😉

Ratelet is shown in your statusbar and gives you easy access to the rating of the current song and iTunes volume.

Ratelet will automatically hide itself from the statusbar when iTunes is not running and reveal if iTunes starts. You can hide the volume slider if you only need the rating bar.

Quickly changing iTunes volume can be useful especially when you use Airplay or output sound digital because the Mac’s volume buttons only affect analog output volume!

 

 

 

 

App development

NSStatusItem highlight custom Button workaround!

I’m building a Menulet with multiple buttons. Since these buttons do no inherit the default behavior of getting blue when clicked, i searched for a quick solution. Since i don’t know how to implement a custom cell and had no ambition to do so, i finally found a nice and quick solution.

The following lines make shure the button won’t show a white background which is default for every buttonType but NSMomentaryChangeButton.

  1. //make sure button won’t show the “alternateImage” when selected
  2. [configureItem setButtonType:NSMomentaryChangeButton];
  3. [configureItem setBezelStyle:NSRegularSquareBezelStyle];
  4. [configureItem setBordered:NO];

Then the next few lines are called in the MenuItem’s delegate every time the menu will open or close.

  1. #pragma mark menu delegate
  2. (void)menuWillOpen:(NSMenu *)menu {
  3.         [[configureItem cell] setBackgroundColor:[NSColor selectedMenuItemColor]];
  4. }
  5. (void)menuDidClose:(NSMenu *)menu {
  6.         [[configureItem cell] setBackgroundColor:[NSColor clearColor]];
  7.     [menu release];
  8. }
App development

Menulet for iTunes volume

Menulet to control iTunes volume

Just needed a quick access to control uatunes volume since this also applies to the airplay volume. Here’s a small Menulet which offers a slider to control it. Have fun 😉

Download/a>

I used the ScriptingBridge and this article to build the app!

App development

Solution: UIScrollview that doesn’t interfere with content touches

Hi there, i’m currently working on a drawing-application which uses a scrollview to navigate threw the content. With the normal UIScrollview you will definitely run into problems!

The problem is that UIScrollView will only deliver taps but no movement gestures to it’s content.

1) So i was looking for a solution to pan and zoom with 2 fingers and interact with the content-view with one finger.

2) Also it should be possible to have a period of time in which the 1 finger touch event can be canceled by the two finger events. This is useful since people tend to start with one finger followed by the second one a little later.

3)Also it should be possible to touch the screen with a second finger without canceling the content interaction!

The idea was to filter all the touch-events, but since UIScrollView uses gesture recognizers (namely UIScrollViewPanGestureRecognizer which inherits from UIPanGestureRecognizer) you can’t filter these touches since there aren’t any but the first touchesBegan…

To solve these problems i did the following:

  1. Set setCanCancelContentTouches:NO when you detect a 1 touch event in touchesBegan: works but you still have the problem with 2).
    • Solution: Use a timer to set setCanCancelContentTouches:NO after a period like 0.1 seconds or so
      • Problem: In the first moments the gesture recognizer will interfere with your touch movements and use them instead
        • Solution: Reconfigure all pangesture recognizers to only accept 2 touch events
Note that i check the gesture recognizers against isKindOfClass not isMemberOfClass since UIScrollViewPanGestureRecognizer inherits from UIPanGestureRecognizer.
Also dont worry about touchFilter! This is the contentview, which in fact doesn’t filter the touches but uses the prefiltered ones!
When the user starts with a one finger gesture and (before timer fired) continues with a second one, beganTouches: and canceledTouches: will be called in this order. The number of touches in canceledTouches will therefore be 2; your contentview must be aware of this, cause if it ignores two touch events it won’t get the canceled event which could result in quite strange behavior.
  1. #import “JWTwoFingerScrollView.h”
  2. @implementation JWTwoFingerScrollView
  3. #pragma mark –
  4. #pragma mark Event Passing
  5. (id)initWithCoder:(NSCoder *)coder {
  6. self = [super initWithCoder:coder];
  7. if (self) {
  8. for (UIGestureRecognizer* r in self.gestureRecognizers) {
  9. NSLog(@“%@”,[r class]);
  10. if ([r isKindOfClass:[UIPanGestureRecognizer class]]) {
  11. [((UIPanGestureRecognizer*)r) setMaximumNumberOfTouches:2];
  12. [((UIPanGestureRecognizer*)r) setMinimumNumberOfTouches:2];
  13. }
  14. }
  15. }
  16. return self;
  17. }
  18. (void)firstTouchTimerFired:(NSTimer*)timer {
  19. [self setCanCancelContentTouches:NO];
  20. }
  21. (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  22. [self setCanCancelContentTouches:YES];
  23. if ([event allTouches].count == 1){
  24. touchesBeganTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(firstTouchTimerFired:) userInfo:nil repeats:NO];
  25. [touchesBeganTimer retain];
  26. [touchFilter touchesBegan:touches withEvent:event];
  27. }
  28. }
  29. (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  30. [touchFilter touchesMoved:touches withEvent:event];
  31. }
  32. (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  33. NSLog(@“ended %i”,[event allTouches].count);
  34. [touchFilter touchesEnded:touches withEvent:event];
  35. }
  36. (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  37. NSLog(@“canceled %i”,[event allTouches].count);
  38. [touchFilter touchesCancelled:touches withEvent:event];
  39. }

It really took me ages to get this running, there were plenty other solutions which all didnt meet my needs. Some used a timer but delayed the first touch event, others allowed only one touch event but interrupted as soon as there was a two finger touch event. Some threads on stackoverflow might be interesting for you, since it also helped me to get this solution working!

http://stackoverflow.com/questions/787212/scrolling-with-two-fingers-with-a-uiscrollview

if you want, download  the files!

App development

iScrape 0.4 drags and drops images, ignores blacklisted folders

Finally there is iScrape 0.4 with many more bugs and some new features. Please report me if you find any bugs or have any other suggestions. Some settings in preference pane are disabled since they aren´t implemented completely.

http://dl.dropbox.com/u/3588714/iScrape%200.4.zip

App development

Dragging files available in future (NSFilePromise)

While implementing drag&drop capability i was searching for a way to drag a file from a custom view without having any data to write to the current pasteboard. In fact the data has a name but no content at the moment of drag.

In iScrape I added the functionlity of dragging from the preview view to any directory in finder. When the user releases the mouse the data isn´t available yet but must be downloaded and written in the target location. Apple’s API offers a smart solution for this issue.

Main steps are:

1. Register your view as a dragging source by registering for every filetype it can provide to a drop destination. Since we don´t provide any certain data but files which will be created in future we must register for NSFilenamesPboardType.The best place to do this is init method:

[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];

2. implement two delegate methods which get called one after the other:

-(NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination;  

– (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation (NSDragOperation)operation;

3. Initiate a drag in mouseDown: method using dragPromisedFilesofType: fromRect: source: slideBack: event: where source is the object which implements the second method in 2.

To give you an idea of how to inititate a drag here you have my mouseDown: implementation
The first delegate method gets called after the drag has dropped onto a destination that accepts NSFilenamsPboardType drops.
The sender asks you to return the filenames of  files you want to create. The sender also provides the droplocation (dropDestination) where you ended the drag or released the mouse. It couldn´t be easier to get the droplocation, but don´t forget to store it because this is the place where you´ll have to save the files…
…Later when draggedImage: endedAt: operation: get’s called: create your data (e.g. download an image from the web) and write the file to the dropLocation. I use something like this where imageURL is the stored dropLocation extended by the filename of the image.
My DownloadController synchronously loads the file from the web and stores it using the static writeFile… method. But since i use dispatch_async without any reference to other objects used in meantime, it´s a thread-safe async download.
I hopefully could help you.