Drag an UIImageView from UIscrollView to another view - objective-c

I crawled all the web, but nothing.
I have a for loop to create uiimageview inside a uiscrollview controller.
It's a collection of images that you can scroll horizontally only.
for (int i = 0; i < NIMAGES; i++) {
NSString *filename = [NSString stringWithFormat:#"image%d.png", i+1];
ScrollerImage *iv = [[ScrollerImage alloc] initWithNibName:#"ScrollerImage" bundle:nil];
[iv initWithImage:[UIImage imageNamed:filename]];
iv.frame = CGRectMake(i * 320.0f, 0.0f, 320.0f, 150.0f);
iv.exclusiveTouch = YES;
iv.userInteractionEnabled = YES;
[sv addSubview:iv];
[iv release];
}
The ScrollerImage class is an UIImageView controller
#import <UIKit/UIKit.h>
#interface ScrollerImage : UIImageView {
}
#end
Now, how can I drag (not necessary a move, also a copy of view, or an id of view, anything that permit me to identify the image) an image from the scroller to another view?
It's like a shopping cart, where you can drag items at the bottom view (cart).
Like this:

This is an alternative, using UIGraphicsGetImageFromCurrentImageContext()
You can grab an image from currentScreen on click or after some events. Next renderize it to screen and apply a Pan gesture to move around the screen.
UIView *_DraggableView;
UIImageView *_imgvcChild1;
sample code:
- ( void ) onScrollviewClickOrOtherEvent
{
UIGraphicsBeginImageContext ( _DraggableView.frame.size );
[ _DraggableView.layer renderInContext:UIGraphicsGetCurrentContext() ];
UIImage *grab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if ( _imgvcChild1 ) [ _imgvcChild1 removeFromSuperview ];
_imgvcChild1 = [[ UIImageView alloc ] initWithImage:grab ];
_imgvcChild1.frame = _DraggableView.frame;
_imgvcChild1.userInteractionEnabled = YES;
[ self.view addSubview:_imgvcChild1 ];
UIPanGestureRecognizer *pan = [[ UIPanGestureRecognizer alloc ] initWithTarget:self action:#selector(moveObject:) ];
[ pan setMinimumNumberOfTouches:1 ];
[ _imgvcChild1 addGestureRecognizer:pan ];
}
- (void) moveObject:(UIPanGestureRecognizer *)pan
{
_imgvcChild1.center = [ pan locationInView:_imgvcChild1.superview ];
}
--- EDIT ---
A sample project on github: https://github.com/elpsk/Shopping-Cart

I've been trying to implement this in an app of mine and I had to just give up. I subclassed UIImageview like you have as well(which is a good option) and all my images are on a UIView that is a subclass of UIScrollView. You can pass your viewcontroller to the images themselves and change which view the imageview is a subview to when it reaches a certain boundary (I overrided the touchesMoved on the ImageViews themselves). The problem with this is it doesn't drag continously. The user has to literally select the image again when they dragged the image across a certain point because you're literally switching the uiimageview from one view to another.
I hope this gives you some insight at least, I really haven't found a solution to this issue. You'd think a lot of IPad apps would be paving the way to drag images all over different views and more tutorials appear on the issue!

Related

UIImageView Won't Show When ViewController Added As Subview

My main aim is to have one background for all of my ViewControllers. Every ViewController I have has a clear background.
To do this, I have made one UIViewController (called backgroundViewController) that will act as the subview to all my other ViewControllers. It has one UIImageView which displays this particular background. I will then add this backgroundViewController as a subview of all my other ViewControllers.
The problem is - this imageView won't show as a subview!
This is how I display the imageView:
- (void)viewDidLoad
{
if ([musicPlayer playbackState] == MPMusicPlaybackStateStopped) {
UIImage *artworkBackgroundView;
artworkBackgroundView = [UIImage imageNamed:#"noArtworkBackground"];
UIImage *effectImage = nil;
backgroundView.image = effectImage;
[backgroundView setImage:artworkBackgroundView];
backgroundView.image = effectImage;
}
}
- (void) handle_NowPlayingItemChanged: (id) notification
{
if ([musicPlayer playbackState] != MPMusicPlaybackStateStopped) {
// Get artwork for current now playing item
MPMediaItem *currentItem = [musicPlayer nowPlayingItem];
MPMediaItemArtwork *artwork = [currentItem valueForProperty: MPMediaItemPropertyArtwork];
UIImage *artworkBackgroundView = [artwork imageWithSize: CGSizeMake(618, 618)];
if (!artworkImage) {
artworkBackgroundView = [UIImage imageNamed:#"noArtworkBackground"];
artworkImage = [UIImage imageNamed:#"noArtwork"];
}
[backgroundView setImage:artworkBackgroundView];
}
}
As you can see, backgroundView changes each time the music player skips song.
To test that backgroundViewController does show as a subview, I added another imageView and changed its image to a static .png in Interface Builder, and that shows correctly as a subview.
This is how I make it a subview for other ViewControllers:
backgroundViewController *backgroundVC = [[backgroundViewController alloc] initWithNibName:#"backgroundViewController" bundle:nil];
[self.view insertSubview:backgroundVC.view atIndex:0];
What I want is the UIImageView called backgroundView to show up when it is being called as subview.
I have tested that backgroundView does change according to what song is playing, and it works correctly.
What am I doing wrong? backgroundView refuses to show up as a subview?! I've searched a ton about adding ViewControllers as subviews but I can't find a similar problem.
Any help would be much appreciated, thanks! :)
This worked for me:
backgroundViewController *backgroundVC = [[backgroundViewController alloc] initWithNibName:#"backgroundViewController" bundle:nil];
[self addChildViewController:backgroundVC];
[self.view addSubview:backgroundVC.view];
I found the solution here. I need to read more about 'Custom Containers'.

Add a floating bar to a scroll view like in the Facebook iOS app's timeline

I've been playing around with adding different interactions to a test project of mine, and I'm having trouble adding something like Facebook's post status bar, the one that sits on the timeline scrollview and scrolls with the scrollview when you're scrolling down the view but stays stuck under the navbar when you're scrolling up it.
I've been creating a separate UIViewController (not UIView) and adding it as a subview to the main ViewController. I'm not exactly too sure where to go from there though... How does the new view scroll with the scrollview? Should I even be using a separate viewcontroller?
Any help would be greatly appreciated! Thanks!
Here's some sample code you can use to get started, just add it to your view controller. It uses a generic UIView for the floating bar and a generic UIScrollView for the scroll view but you can change that to whatever you want.
#interface BNLFDetailViewController () <UIScrollViewDelegate> {
UIScrollView *_scrollView;
UIView *_floatingBarView;
CGFloat _lastOffset;
}
#end
And in the #implementation add:
- (void)viewDidLoad {
[super viewDidLoad];
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
_scrollView.delegate = self;
_scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height * 2);
[self.view addSubview:_scrollView];
CGRect f = self.view.bounds;
f.size.height = kFloatingBarHeight;
_floatingBarView = [[UIView alloc] initWithFrame:f];
_floatingBarView.backgroundColor = [UIColor blackColor];
[self.view addSubview:_floatingBarView];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == _scrollView) {
CGFloat offsetChange = _lastOffset - scrollView.contentOffset.y;
CGRect f = _floatingBarView.frame;
f.origin.y += offsetChange;
if (f.origin.y < -kFloatingBarHeight) f.origin.y = -kFloatingBarHeight;
if (f.origin.y > 0) f.origin.y = 0;
if (scrollView.contentOffset.y <= 0) f.origin.y = 0; //Deal with "bouncing" at the top
if (scrollView.contentOffset.y + scrollView.bounds.size.height >= scrollView.contentSize.height) f.origin.y = -kFloatingBarHeight; //Deal with "bouncing" at the bottom
_floatingBarView.frame = f;
_lastOffset = scrollView.contentOffset.y;
}
}
You should be doing it as a UIView, not a UIViewController. The general rule in iOS development is that view controllers take up the entire screen and views are used for "subviews" that take up part of the screen (though this is less the case for iPad). Either way, a UIViewController has it's own lifecycle (willAppear, didAppear, etc.) which is not needed/wanted for the floating bar so it should definitely be a UIView.

NSScrollView doesn't scroll

I'm porting an iPhone app that uses this tutorial into a Mac app. I've been able to get text to display in NSScrollView, but I can't get the text to scroll and I hope someone knows what I am missing.
In the iPhone app, CTView is subclassed from UIScrollView
CTView.h:
#interface CTView : UIScrollView <UIScrollViewDelegate> {
1) But there is no NSScrollViewDelegate in AppKit. Is the missing delegate interface to NSScrollView the problem?
So for the Mac app, CTView.h looks like the following:
#interface CTView : NSScrollView
In CTView.m I have
[self setHasHorizontalScroller:YES];
[self setAcceptsTouchEvents:YES];
Also inside CTView.m I create a NSMutableArray of frames as
self.frames = [NSMutableArray array];
and fill the array up with frames. I also set the frame for the document view for the complete larger image that I want to be able to scroll through as:
[[self contentView] setFrame:[self bounds]];
[self.documentView setFrame: NSMakeRect(0, 0, totalPages * self.bounds.size.width, textFrame.size.height)];
The first frame is visible in the CTView, but when I move the horizontal scroll bar, the second frame doesn't show up in CTView.
I'm using Xcode 4.3.3 and Mac OS X 10.7.4.
Does anyone know what I need to do differently from UIScrollView for NSScrollView to be able to scroll through the frames?
Finally figured it out. Whew!
1) NSScrollView needs a view for its documentView, where the documentView is what is scrolled over. So in the nib I created a NSScrollView that contained a view for the documentView and set the documentView to that view as shown by the code in CTView.m
NSArray *viewArray = [self subviews];
NSArray *docViewArray = [[viewArray objectAtIndex:0] subviews];
NSView *docView = [docViewArray objectAtIndex:0];
[self setDocumentView:docView];
2) Then I added the array of frames, that would normally be added to UIScrollView, to the documentView of NSScrollView. The array of frames consists of multiple elements of content.
while (textPos < [attString length]) {
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(textPos, 0), innerPath, NULL);
CFRange frameRange = CTFrameGetVisibleStringRange(frame);
CTColumnView* content = [[[CTColumnView alloc] initWithFrame: NSMakeRect(0, 0, self.contentSize.width, self.contentSize.height)] autorelease];
[docView addSubview: content]; // <<-- Here is where the frames are added to the documentView
textPos += frameRange.length;
}
And that did the trick.

How to increase the height of NSTableHeaderView?

I need to implement a headerview with specific size and gradient. I have to insert images in certain cells of the headerview.Tried to create the cells for the headerview using the following code,but i was not able to customize the headerview.
[[tableColumn headerCell] setImage:[NSImage imageNamed:#"sampleHeader"]];
If I use the overridden subclass of headerview, I was not able to view the images or text in the header cell.Please provide me any pointers to solve this issue.
I was able to insert images and text by subclassing the NSTableHeaderCell.How to increase height of the NSTableHeaderView?
If I subclass both NSTableHeaderView and NSTableHeaderCell , was not able to view anything in the
headercell.I used the following code for setting headerview and headercell
[tableView setHeaderView:CustomHeaderView];
[tableColumn setHeaderCell:[[[CustomHeaderTableCell alloc] initImageCell:
[NSImage imageNamed:#"sample"]]autorelease]];
I have the same issue as given in the below url
http://lists.apple.com/archives/cocoa-dev/2002/Jun/msg00331.html
You don't need to subclass NSTableHeaderView.
I was able to change the height of the header view using the following snippet in the controller class:
-(void)awakeFromNib {
NSRect frame = tableView.headerView.frame;
frame.size.height = 26;
tableView.headerView.frame = frame;
}
It should be noted that the scroll view takes care of the layout. It automatically changes the frame of the headerView as necessary, but leaves the height intact. Resizing the clip view etc as suggested in the other answer is not necessary.
You can also create a NSTableHeaderView object, initialize it with a frame(rect with height and width) and set that NSTableHeaderView object to your table view.
NSTableHeaderView *tableHeaderView = [[NSTableHeaderView alloc] initWithFrame:NSMakeRect(0, 0, 120, 60)];
[myTableView setHeaderView:tableHeaderView];
[tableHeaderView release];
Following link helped me in solving the issue.
http://lists.apple.com/archives/cocoa-dev/2003/Feb/msg00676.html
You need to set the Frame for NSClipView, NSTableHeaderView and the CornerView
This is how I implemented the same in Code.
for(NSView * subview in [topScrollView subviews])
{
for(NSView * subSubView in [subview subviews])
{
if([[subSubView className] isEqualToString:#"NSTableHeaderView"] && [[subview className] isEqualToString:#"NSClipView"])
{
[subSubView setFrameSize:NSMakeSize(subSubView.frame.size.width, subSubView.frame.size.height+5)];//HeaderView Frame
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)];//ClipView Frame
}
}
if ([[subview className] isEqualToString:#"_NSCornerView"])
{
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)]; //CornerView Frame
}
}

How do I pass the user touch from one object to another object?

I am developing an application that allows the user at a certain point to drag and drop 10 images around. So there are 10 images, and if he/she drags one image onto another, these two are swapped.
A screenshot of how this looks like:
So when the user drags one photo I want it to reduce its opacity and give the user a draggable image on his finger which disappears again if he drops it outside of any image.
The way I have developed this is the following. I have set a UIPanGesture for these UIImageViews as:
for (UIImageView *imgView in editPhotosView.subviews) {
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(photoDragged:)];
[imgView addGestureRecognizer:panGesture];
imgView.userInteractionEnabled = YES;
[panGesture release];
}
Then my photoDragged: method:
- (void)photoDragged:(UIPanGestureRecognizer *)gesture {
UIView *view = gesture.view;
if ([view isKindOfClass:[UIImageView class]]) {
UIImageView *imgView = (UIImageView *)view;
if (gesture.state == UIGestureRecognizerStateBegan) {
imgView.alpha = 0.5;
UIImageView *newView = [[UIImageView alloc] initWithFrame:imgView.frame];
newView.image = imgView.image;
newView.tag = imgView.tag;
newView.backgroundColor = imgView.backgroundColor;
newView.gestureRecognizers = imgView.gestureRecognizers;
[editPhotosView addSubview:newView];
[newView release];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gesture translationInView:view.superview];
[view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
[gesture setTranslation:CGPointZero inView:view.superview];
}
else { ... }
}
}
Thus as you see I add a new UIImageView with 0.5 opacity on the same spot as the original image when the user starts dragging it. So the user is dragging the original image around. But what I want to do is to copy the original image when the user drags it and create a "draggable" image and pass that to the user to drag around.
But to do that I have to pass the user touch on to the newly created "draggable" UIImageView. While it's actually set to the original image (the one the user touches when he starts dragging).
So my question is: How do I pass the user's touch to another element?.
I hope that makes sense. Thanks for your help!
Well, you can pass the UIPanGestureRecognizer object to another object by creating a method in your other object which takes the gesture recognizer as a parameter.
- (void)myMethod:(UIPanGestureRecognizer *)gesture
{
// Do stuff
}
And call from your current gesture recognizer using....
[myOtherObject myMethod:gesture];
Not entirely sure I'm understanding your question here fully. :-/
Maybe:
[otherObject sendActionsForControlEvents:UIControlEventTouchUpInside];
Or any other UIControlEvent
In the end I decided to indeed drag the original image and leave a copy at the original place.
I solved the issue with the gesture recognizers I was having by re-creating them and assigning them to the "copy", just like PragmaOnce suggested.