I'd like to know if it's possible to insert multiple images into a "container" in objective-c.
For example, if i had an image of a front wheel, a back wheel, and the body of a car. Could i position these all in a container so that when i move or tranform (scale, rotate) the container, all the images inside will perform the that action? At the same time i could have one or more of the images inside the container performing an animation.
I hope that's clear and i've properly explained what i'm trying to do.
Not sure about container,
But I have another approach,
Why don't you make one custom view and put all the images inside that, and can perform all the operation over that view,
I am posting some important method, let me know if its works for you, i may share some more detail
-(void)refreshSession{
// check all the added view, if needs to be displayed, then displayed them.
int visibleViews = [self getVisibleViews];
if(visibleViews == 0 ){
// if no more views to be displayed, then then let it not be visible,
[[self window] orderOut:self];
// do it will not be visible to anyone
return;
}
[self resize:visibleViews];
int viewsPerSide = [self getViewsPerRow];//ceil(sqrt(count));
int count = [pViewArray count];
int viewIndex=0;
NSInteger index = 0;
NSPoint curPoint = NSZeroPoint;
// Starting at the bottom left corner
// we should check all visible views
for ( int i = 0; i < count ; i++ ) {
// it will give only the view that needs to be shown
NSView *subview = [self getViewAtIndex:i];//(NSView *)[pViewArray objectAtIndex:i];
// is this visible
if(![self isViewVisibleAtIndex:i])
continue;
// is view visible
// if we have view to display then lets display it.
if(subview){
//[[[self window] contentView]setBackgroundColor:[NSColor blackColor]];
[[[self window]contentView] addSubview:subview ];
[self checkMask:viewIndex view:subview];
viewIndex++;
// let all the frame have same rect, regardless what are those
NSRect frame = NSMakeRect(curPoint.x, curPoint.y, BOXWIDTH, BOXHEIGHT);
// add them into the subview
NSRect newFrame = [self integralRect:frame];
[subview setFrame:newFrame];
[[subview animator] setFrame:[self integralRect:frame]];
[self setSubViewMask:subview ContentView:[[self window]contentView]];
// Now set the point
curPoint.x += BOXWIDTH + SEPARATION; // Stack them horizontally
if ((++index) % viewsPerSide == 0) { // And if we have enough on this row, move up to the next
curPoint.x = 0;
curPoint.y += BOXHEIGHT + SEPARATION;
}
}
}
NSArray *pSubViewsArray = [[[self window]contentView]subviews];
int arrayCount = [pSubViewsArray count];
debugLog<<"Array Count"<<arrayCount<<endl;
}
Related
I have a UICollectionView with 6 pages, and paging enabled, and a UIPageControl. What I want is, when I came to the last page, if I drag to right, UICollectionView reloads from first page seamlessly.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
// The key is repositioning without animation
if (collectionView.contentOffset.x == 0) {
// user is scrolling to the left from image 1 to image 10.
// reposition offset to show image 10 that is on the right in the scroll view
[collectionView scrollRectToVisible:CGRectMake(collectionView.frame.size.width*(pageControl.currentPage-1),0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];
}
else if (collectionView.contentOffset.x == 1600) {
// user is scrolling to the right from image 10 to image 1.
// reposition offset to show image 1 that is on the left in the scroll view
[collectionView scrollRectToVisible:CGRectMake(0,0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];
}
pageControlUsed = NO;
}
It doesn't work like I want. What can I do?
Here's what I ended up with for my UICollectionView (horizontal scrolling like the UIPickerView):
#implementation UIInfiniteCollectionView
- (void) recenterIfNecessary {
CGPoint currentOffset = [self contentOffset];
CGFloat contentWidth = [self contentSize].width;
// don't just snap to center, since this might be done in the middle of a drag and not aligned. Make sure we account for that offset
CGFloat offset = kCenterOffset - currentOffset.x;
int delta = -round(offset / kCellSize);
CGFloat shift = (offset + delta * kCellSize);
offset += shift;
CGFloat distanceFromCenter = fabs(offset);
// don't always recenter, just if we get too far from the center. Eliza recommends a quarter of the content width
if (distanceFromCenter > (contentWidth / 4.0)) {
self.contentOffset = CGPointMake(kCenterOffset, currentOffset.y);
// move subviews back to make it appear to stay still
for (UIView *subview in self.subviews) {
CGPoint center = subview.center;
center.x += offset;
subview.center = center;
}
// add the offset to the index (unless offset is 0, in which case we'll assume this is the first launch and not a mid-scroll)
if (currentOffset.x > 0) {
int delta = -round(offset / kCellSize);
// MODEL UPDATE GOES HERE
}
}
}
- (void) layoutSubviews { // called at every frame of scrolling
[super layoutSubviews];
[self recenterIfNecessary];
}
#end
Hope this helps someone.
I've been using the Street Scroller sample to create an infinite scroller for images. That works fine until I wanted to set pagingEnabled = YES; Tried tweaking around the recenterIfNecessary code and finally realized that it's the contentOffset.x that has to match the frame of the subview that i want visible when paging stops. This really isn't going to work in recenterIfNecessary since you have no way of knowing it will get called from layoutSubviews. If you do get it adjusted right, the subview may pop out from under your finger. I do the adjustment in scrollViewDidEndDecelerating. So far I haven't had problems with scrolling fast. It will work and simulate paging even when pagingEnabled is NO, but it looks more natural with YES.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[super scrollViewDidEndDecelerating:scrollView];
CGPoint currentOffset = [self contentOffset];
// find the subview that is the closest to the currentOffset.
CGFloat closestOriginX = 999999;
UIView *closestView = nil;
for (UIView *v in self.visibleImageViews) {
CGPoint origin = [self.imageContainerView convertPoint:v.frame.origin toView:self];
CGFloat distanceToCurrentOffset = fabs(currentOffset.x - origin.x);
if (distanceToCurrentOffset <= closestOriginX) {
closestView = v;
closestOriginX = distanceToCurrentOffset;
}
}
// found the closest view, now find the correct offset
CGPoint origin = [self.imageContainerView convertPoint:closestView.frame.origin toView:self];
CGPoint center = [self.imageContainerView convertPoint:closestView.center toView:self];
CGFloat offsetX = currentOffset.x - origin.x;
// adjust the centers of the subviews
[UIView animateWithDuration:0.1 animations:^{
for (UIView *v in self.visibleImageViews) {
v.center = [self convertPoint:CGPointMake(v.center.x+offsetX, center.y) toView:self.imageContainerView];
}
}];
}
I have not used UICollectionView for infinite scrolling, but when doing it with a UIScrollView you first adjust your content offset (instead of using scrollRectToVisible) to the location you want. Then, you loop through each subview in your scroller and adjust their coordinates either to the right or left based on the direction the user was scrolling. Finally, if either end is beyond the bounds you want them to be, move them to the far other end. Their is a very good WWDC video from apple about how to do infinite scrolling you can find here: http://developer.apple.com/videos/wwdc/2012/
Typically, the scrollView's content view is a rectangle. But I would like to implement that is not a rectangle.... For example....
The yellow, Grid 6 is the current position...Here is the example flow:
User swipe to left. (cannot scroll to left) Current: 6.
User swipe to right. (scroll to right) Current: 7.
User swipe to down. (scroll to down) Current: 8.
User swipe to down. (cannot scroll to down) Current: 8.
As you can see, the Content view of the scrollView is not rectangle. Any ideas on how to implement it? Thanks.
This is an interesting idea to implement. I can think of a few approaches that might work. I tried out one, and you can find my implementation in my github repository here. Download it and try it out for yourself.
My approach is to use a normal UIScrollView, and constrain its contentOffset in the delegate's scrollViewDidScroll: method (and a few other delegate methods).
Preliminaries
First, we're going to need a constant for the page size:
static const CGSize kPageSize = { 200, 300 };
And we're going to need a data structure to hold the current x/y position in the grid of pages:
typedef struct {
int x;
int y;
} MapPosition;
We need to declare that our view controller conforms to the UIScrollViewDelegate protocol:
#interface ViewController () <UIScrollViewDelegate>
#end
And we're going to need instance variables to hold the grid (map) of pages, the current position in that grid, and the scroll view:
#implementation ViewController {
NSArray *map_;
MapPosition mapPosition_;
UIScrollView *scrollView_;
}
Initializing the map
My map is just an array of arrays, with a string name for each accessible page and [NSNull null] at inaccessible grid positions. I'll initialize the map from my view controller's init method:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
[self initMap];
}
return self;
}
- (void)initMap {
NSNull *null = [NSNull null];
map_ = #[
#[ #"1", null, #"2"],
#[ #"3", #"4", #"5" ],
#[ null, #"6", #"7" ],
#[ null, null, #"8" ],
];
mapPosition_ = (MapPosition){ 0, 0 };
}
Setting up the view hierarchy
My view hierarchy will look like this:
top-level view (gray background)
scroll view (transparent background)
content view (tan background)
page 1 view (white with a shadow)
page 2 view (white with a shadow)
page 3 view (white with a shadow)
etc.
Normally I'd set up some of my views in a xib, but since it's hard to show xibs in a stackoverflow answer, I'll do it all in code. So in my loadView method, I first set up a “content view” that will live inside the scroll view. The content view will contain a subviews for each page:
- (void)loadView {
UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [map_[0] count] * kPageSize.width, map_.count * kPageSize.height)];
contentView.backgroundColor = [UIColor colorWithHue:0.1 saturation:0.1 brightness:0.9 alpha:1];
[self addPageViewsToContentView:contentView];
Then I'll create my scroll view:
scrollView_ = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kPageSize.width, kPageSize.height)];
scrollView_.delegate = self;
scrollView_.bounces = NO;
scrollView_.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleRightMargin
| UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleBottomMargin);
I add the content view as a subview of the scroll view and set up the scroll view's content size and offset:
scrollView_.contentSize = contentView.frame.size;
[scrollView_ addSubview:contentView];
scrollView_.contentOffset = [self contentOffsetForCurrentMapPosition];
Finally, I create my top-level view and give it the scroll view as a subview:
UIView *myView = [[UIView alloc] initWithFrame:scrollView_.frame];
[myView addSubview:scrollView_];
myView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
self.view = myView;
}
Here's how I compute the scroll view's content offset for the current map position, and for any map position:
- (CGPoint)contentOffsetForCurrentMapPosition {
return [self contentOffsetForMapPosition:mapPosition_];
}
- (CGPoint)contentOffsetForMapPosition:(MapPosition)position {
return CGPointMake(position.x * kPageSize.width, position.y * kPageSize.height);
}
To create subviews of the content view for each accessible page, I loop over the map:
- (void)addPageViewsToContentView:(UIView *)contentView {
for (int y = 0, yMax = map_.count; y < yMax; ++y) {
NSArray *mapRow = map_[y];
for (int x = 0, xMax = mapRow.count; x < xMax; ++x) {
id page = mapRow[x];
if (![page isKindOfClass:[NSNull class]]) {
[self addPageViewForPage:page x:x y:y toContentView:contentView];
}
}
}
}
And here's how I create each page view:
- (void)addPageViewForPage:(NSString *)page x:(int)x y:(int)y toContentView:(UIView *)contentView {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectInset(CGRectMake(x * kPageSize.width, y * kPageSize.height, kPageSize.width, kPageSize.height), 10, 10)];
label.text = page;
label.textAlignment = NSTextAlignmentCenter;
label.layer.shadowOffset = CGSizeMake(0, 2);
label.layer.shadowRadius = 2;
label.layer.shadowOpacity = 0.3;
label.layer.shadowPath = [UIBezierPath bezierPathWithRect:label.bounds].CGPath;
label.clipsToBounds = NO;
[contentView addSubview:label];
}
Constraining the scroll view's contentOffset
As the user moves his finger around, I want to prevent the scroll view from showing an area of its content that doesn't contain a page. Whenever the scroll view scrolls (by updating its contentOffset), it sends scrollViewDidScroll: to its delegate, so I can implement scrollViewDidScroll: to reset the contentOffset if it goes out of bounds:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint contentOffset = scrollView_.contentOffset;
First, I want to constrain contentOffset so the user can only scroll horizontally or vertically, not diagonally:
CGPoint constrainedContentOffset = [self contentOffsetByConstrainingMovementToOneDimension:contentOffset];
Next, I want to constrain contentOffset so that it only shows parts of the scroll view that contain pages:
constrainedContentOffset = [self contentOffsetByConstrainingToAccessiblePoint:constrainedContentOffset];
If my constraints modified contentOffset, I need to tell the scroll view about it:
if (!CGPointEqualToPoint(contentOffset, constrainedContentOffset)) {
scrollView_.contentOffset = constrainedContentOffset;
}
Finally, I update my idea of the current map position based on the (constrained) contentOffset:
mapPosition_ = [self mapPositionForContentOffset:constrainedContentOffset];
}
Here's how I compute the map position for a given contentOffset:
- (MapPosition)mapPositionForContentOffset:(CGPoint)contentOffset {
return (MapPosition){ roundf(contentOffset.x / kPageSize.width),
roundf(contentOffset.y / kPageSize.height) };
}
Here's how I constrain the movement to just horizontal or vertical and prevent diagonal movement:
- (CGPoint)contentOffsetByConstrainingMovementToOneDimension:(CGPoint)contentOffset {
CGPoint baseContentOffset = [self contentOffsetForCurrentMapPosition];
CGFloat dx = contentOffset.x - baseContentOffset.x;
CGFloat dy = contentOffset.y - baseContentOffset.y;
if (fabsf(dx) < fabsf(dy)) {
contentOffset.x = baseContentOffset.x;
} else {
contentOffset.y = baseContentOffset.y;
}
return contentOffset;
}
Here's how I constrain contentOffset to only go where there are pages:
- (CGPoint)contentOffsetByConstrainingToAccessiblePoint:(CGPoint)contentOffset {
return [self isAccessiblePoint:contentOffset]
? contentOffset
: [self contentOffsetForCurrentMapPosition];
}
Deciding whether a point is accessible turns out to be the tricky bit. It's not enough to just round the point's coordinates to the nearest potential page center and see if that rounded point represents an actual page. That would, for example, let the user drag left/scroll right from page 1, revealing the empty space between pages 1 and 2, until page 1 is half off the screen. We need to round the point down and up to potential page centers, and see if both rounded points represent valid pages. Here's how:
- (BOOL)isAccessiblePoint:(CGPoint)point {
CGFloat x = point.x / kPageSize.width;
CGFloat y = point.y / kPageSize.height;
return [self isAccessibleMapPosition:(MapPosition){ floorf(x), floorf(y) }]
&& [self isAccessibleMapPosition:(MapPosition){ ceilf(x), ceilf(y) }];
}
Checking whether a map position is accessible means checking that it's in the bounds of the grid and that there's actually a page at that position:
- (BOOL)isAccessibleMapPosition:(MapPosition)p {
if (p.y < 0 || p.y >= map_.count)
return NO;
NSArray *mapRow = map_[p.y];
if (p.x < 0 || p.x >= mapRow.count)
return NO;
return ![mapRow[p.x] isKindOfClass:[NSNull class]];
}
Forcing the scroll view to rest at page boundaries
If you don't need to force the scroll view to rest at page boundaries, you can skip the rest of this. Everything I described above will work without the rest of this.
I tried setting pagingEnabled on the scroll view to force it to come to rest at page boundaries, but it didn't work reliably, so I have to enforce it by implementing more delegate methods.
We'll need a couple of utility functions. The first function just takes a CGFloat and returns 1 if it's positive and -1 otherwise:
static int sign(CGFloat value) {
return value > 0 ? 1 : -1;
}
The second function takes a velocity. It returns 0 if the absolute value of the velocity is below a threshold. Otherwise, it returns the sign of the velocity:
static int directionForVelocity(CGFloat velocity) {
static const CGFloat kVelocityThreshold = 0.1;
return fabsf(velocity) < kVelocityThreshold ? 0 : sign(velocity);
}
Now I can implement one of the delegate methods that the scroll view calls when the user stops dragging. In this method, I set the targetContentOffset of the scroll view to the nearest page boundary in the direction that the user was scrolling:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (fabsf(velocity.x) > fabsf(velocity.y)) {
*targetContentOffset = [self contentOffsetForPageInHorizontalDirection:directionForVelocity(velocity.x)];
} else {
*targetContentOffset = [self contentOffsetForPageInVerticalDirection:directionForVelocity(velocity.y)];
}
}
Here's how I find the nearest page boundary in a horizontal direction. It relies on the isAccessibleMapPosition: method, which I already defined earlier for use by scrollViewDidScroll::
- (CGPoint)contentOffsetForPageInHorizontalDirection:(int)direction {
MapPosition newPosition = (MapPosition){ mapPosition_.x + direction, mapPosition_.y };
return [self isAccessibleMapPosition:newPosition] ? [self contentOffsetForMapPosition:newPosition] : [self contentOffsetForCurrentMapPosition];
}
And here's how I find the nearest page boundary in a vertical direction:
- (CGPoint)contentOffsetForPageInVerticalDirection:(int)direction {
MapPosition newPosition = (MapPosition){ mapPosition_.x, mapPosition_.y + direction };
return [self isAccessibleMapPosition:newPosition] ? [self contentOffsetForMapPosition:newPosition] : [self contentOffsetForCurrentMapPosition];
}
I discovered in testing that setting targetContentOffset did not reliably force the scroll view to come to rest on a page boundary. For example, in the iOS 5 simulator, I could drag right/scroll left from page 5, stopping halfway to page 4, and even though I was setting targetContentOffset to page 4's boundary, the scroll view would just stop scrolling with the 4/5 boundary in the middle of the screen.
To work around this bug, we have to implement two more UIScrollViewDelegate methods. This one is called when the touch ends:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[scrollView_ setContentOffset:[self contentOffsetForCurrentMapPosition] animated:YES];
}
}
And this one is called when the scroll view stops decelerating:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGPoint goodContentOffset = [self contentOffsetForCurrentMapPosition];
if (!CGPointEqualToPoint(scrollView_.contentOffset, goodContentOffset)) {
[scrollView_ setContentOffset:goodContentOffset animated:YES];
}
}
The End
As I said at the beginning, you can download my test implementation from my github repository and try it out for yourself.
That's all, folks!
I'm assuming you're using the UIScrollView in paged mode (swipe to show an entire new screen).
With a bit jiggery-pokery you can achieve the effect you want.
The trick is to ensure that whatever square you're currently viewing, you have the UIScrollView configured so that only the visible central view, and the surrounding view that you could scroll too, are added to the scroll view (and at the correct offset). You also must ensure that the size of the scrollable content (and the current offset) is set correctly, to prevent scrolling in a direction that would take you to no content.
Example: suppose you're viewing square 6 currently. At that point, your scroll view would just have 4 views added to it: 4, 5, 6 and 7, in the correct relative offsets. And you set the content size of the scroll view to be equivelant to 2 x 2 squares size. This will prevent scrolling down or to the left (where there are no tiles) but will allow scrolling in the correct direction.
You'll need your delegate to detect scrollViewDidEndDecelerating:. In that instance, you then have to set up your views, content offset, and content size as described above, for the new location.
I have a view which has two labels. When I swipe left I fill next content to label text. Similarly swiping right loads previous content. I want to give an effect to labels like they are scrolling from left or right.
I used a scrollview before but it had a memory problem. So I'm using one view, and swipe gesture loads next or previous content. I want to add scrollview's sliding effect to labels. How can I do that?
I'm not quite sure precisely what effect you're looking for, but you could do something like this, which creates a new, temporary label, puts it off screen, animates the moving it over the label you have on screen, and then when done, resets the old one and deletes the temporary label. This is what a non-autolayout implementation might look like:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UISwipeGestureRecognizer *left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipe:)];
[left setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:left];
// if non-ARC, release it
// [release left];
self.label1.text = #"Mo";
}
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
// create new label
UILabel *tempLabel = [[UILabel alloc] initWithFrame:existingLabel.frame];
[existingLabel.superview addSubview:tempLabel];
tempLabel.text = newText;
// move the new label off-frame to the right
tempLabel.transform = CGAffineTransformMakeTranslation(tempLabel.superview.bounds.size.width, 0);
// animate the sliding of them into place
[UIView animateWithDuration:0.5
animations:^{
tempLabel.transform = CGAffineTransformIdentity;
existingLabel.transform = CGAffineTransformMakeTranslation(-existingLabel.superview.bounds.size.width, 0);
}
completion:^(BOOL finished) {
existingLabel.text = newText;
existingLabel.transform = CGAffineTransformIdentity;
[tempLabel removeFromSuperview];
}];
// if non-ARC, release it
// [release tempLabel];
}
This animation animates the label with respect to its superview. You may want to ensure that the superview is set to "clip subviews". This way, the animation will be constrained to the bounds of that superview, which yields a slightly more polished look.
Note, if using auto layout, the idea is the same (though the execution is more complicated). Basically configure your constraints so new view is off to the right, then, in animation block update/replace the constraints so the original label is off to the left and the new one is in the spot of the original label, and, finally, in the completion block reset the constraints of the original label and remove the temporary label.
By the way, this is all infinitely easier if you're comfortable with one of the built in transitions:
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
[UIView transitionWithView:existingLabel // or try `existingLabel.superview`
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
existingLabel.text = newText;
}
completion:nil];
}
If you prefer animation that behaves more like a scroll view (i.e., with continuous feedback on the gesture), it might look something like the following:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panHandler:)];
[self.view addGestureRecognizer:pan];
self.label1.text = #"Mo";
}
- (void)panHandler:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
_panLabel = [[UILabel alloc] init];
// in my example, I'm just going to toggle the value between Mo and Curly
// you'll presumably set the label contents based upon the direction of the
// pan (if positive, swiping to the right, grab the "previous" label, if negative
// pan, grab the "next" label)
if ([self.label1.text isEqualToString:#"Curly"])
_newText = #"Mo";
else
_newText = #"Curly";
// set the text
_panLabel.text = _newText;
// set the frame to just be off screen
_panLabel.frame = CGRectMake(self.label1.frame.origin.x + self.containerView.frame.size.width,
self.label1.frame.origin.y,
self.label1.frame.size.width,
self.label1.frame.size.height);
[self.containerView addSubview:_panLabel];
_originalCenter = self.label1.center; // save where the original label originally was
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.containerView];
if (translate.x > 0)
{
_panLabel.center = CGPointMake(_originalCenter.x - self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
else
{
_panLabel.center = CGPointMake(_originalCenter.x + self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
}
else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled)
{
CGPoint translate = [sender translationInView:self.containerView];
CGPoint finalNewFieldLocation;
CGPoint finalOriginalFieldLocation;
BOOL panSucceeded;
if (sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStateCancelled)
{
panSucceeded = NO;
}
else
{
// by factoring in the velocity, we can capture a flick more accurately
//
// (by the way, I don't like iOS's velocity, because if you stop moving, it records the velocity
// prior to stopping the move rather than noting that you actually stopped, so I usually calculate my own,
// but I'll leave this as is for purposes of this example)
CGPoint velocity = [sender velocityInView:self.containerView];
if (translate.x < 0)
panSucceeded = ((translate.x + velocity.x * 0.5) < -(self.containerView.frame.size.width / 2));
else
panSucceeded = ((translate.x + velocity.x * 0.5) > (self.containerView.frame.size.width / 2));
}
if (panSucceeded)
{
// if we succeeded, finish moving the stuff
finalNewFieldLocation = _originalCenter;
if (translate.x < 0)
finalOriginalFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
else
finalOriginalFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
}
else
{
// if we didn't, then just return everything to where it was
finalOriginalFieldLocation = _originalCenter;
if (translate.x < 0)
finalNewFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
else
finalNewFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
}
// animate the moving of stuff to their final locations, and on completion, clean everything up
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
_panLabel.center = finalNewFieldLocation;
self.label1.center = finalOriginalFieldLocation;
}
completion:^(BOOL finished) {
if (panSucceeded)
self.label1.text = _newText;
self.label1.center = _originalCenter;
[_panLabel removeFromSuperview];
_panLabel = nil; // in non-ARC, release instead
}
];
}
}
Note, I have put both the original label, as well as the new label being panned on, in a container UIView (called containerView, surprisingly enough), so that I can clip the animation to that container.
I have a imageview in a specific position , i know the position but i want to check if there is an imageview in that position.
please help me out to solve
Here is a helper method to check if two rects overlap:
-(BOOL) rect:(CGRect)rect overlap:(CGRect)anotherRect
{
BOOL xOverlap = ((rect.origin.x >= anotherRect.origin.x) && (rect.origin.x <= anotherRect.origin.x + anotherRect.size.width)) ||
((anotherRect.origin.x >= rect.origin.x) && (anotherRect.origin.x <= rect.origin.x + rect.size.width));
BOOL yOverlap = ((rect.origin.y >= anotherRect.origin.y) && (rect.origin.y <= anotherRect.origin.y + anotherRect.size.height)) ||
((anotherRect.origin.y >= rect.origin.y) && (anotherRect.origin.y <= rect.origin.y + rect.size.height));
return xOverlap && yOverlap;
}
Usage:
You can iterate though all subviews of a view and check if the frame of your view overlaps another view's frame. Suppose you have a property myView then:
for(UIView *v in self.view.subviews)
{
if(v == self.myView) //skip the same view
continue;
if([self rect:self.myView.frame overlap:v.frame])
{
//Do something...
}
}
First you get the array of all your views inside your main view:
NSArray *subviews = [view subviews];
Then, for every view, you can check the frame of every view:
for (int i = 0 ; i < [subviews count]; i++){
CGRect frame = [subviews objectAtIndex:i].frame;
}
You can navigate through the subviews of the superview of that UIImageView in a for loop. And compare the position of each view to that specific UIImageView's position.
But this sounds bad to me. Maybe if you can tell more about the issue, we can help better.
You can use this approach as well:
Since you have said you have position of the ImageView then save this in some CGRect variable.Try this
UIImageView *yourImgView = (UIImageView *)[view subviews];
This will return your imageView . Then you can check its frame with earlier saved position or its visibility depending on your need.
i want to display many images like thumbnails in a scroll view and and i want the images displayed dynamically we scrolls down or left like a table view cells
can u please tell how to that...
Thanks
with the following code..when we scroll the scroll view im calling this code and able to display the images dynamically (which r only visible) but the problem is.. while scrolling with scroll bars im getting the two images..vertically and horizontally..its only happening when i scroll.. can any body help me out please..?
int tileSize;
int imgSize;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
tileSize = 255;
imgSize = 247;
}else{
tileSize = 120;
imgSize = 116;
}
CGRect visibleBounds = [songsContainer bounds];
for (UIView *tile in [songsContainer subviews]) {
CGRect scaledTileFrame =[tile frame];
if (! CGRectIntersectsRect(scaledTileFrame, visibleBounds)) {
for(UIView *view in [tile subviews])
[view removeFromSuperview];
[recycledCells addObject:tile];
[tile removeFromSuperview];
}
}
int maxRow =[songsDict count]-1; // this is the maximum possible row
int maxCol = noOfRowsInCell-1; // and the maximum possible column
int firstNeededRow = MAX(0, floorf(visibleBounds.origin.y / tileSize));
int firstNeededCol = MAX(0, floorf(visibleBounds.origin.x / tileSize));
int lastNeededRow = MIN(maxRow, floorf(CGRectGetMaxY(visibleBounds) / tileSize));
int lastNeededCol = MIN(maxCol, floorf(CGRectGetMaxX(visibleBounds) / tileSize));
NSLog(#".........MaxRow-%d,MaxCol-%d,firstNeddedRow-%d,firstNeededcol-%d,lNR-%d,lNC%d",maxRow, maxCol, firstNeededRow,firstNeededCol,lastNeededRow,lastNeededCol);
// iterate through needed rows and columns, adding any tiles that are missing
for (int row = firstNeededRow; row <= lastNeededRow; row++) {
NSMutableArray *tempArray = (NSMutableArray *)[songsDict objectAtIndex:row];
for (int col = firstNeededCol; col <= lastNeededCol ; col++) {
BOOL tileIsMissing = (firstVisibleRow > row || firstVisibleColumn > col ||
lastVisibleRow < row || lastVisibleColumn < col);
if (tileIsMissing) {
UIView *tile = (UIView *)[self dequeueReusableTile];
if (!tile) {
// the scroll view will handle setting the tile's frame, so we don't have to worry about it
tile = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
tile.backgroundColor = [UIColor clearColor];
}
//tile.image = image for row and col;
// set the tile's frame so we insert it at the correct position
CGRect frame = CGRectMake(tileSize * col, tileSize * row, imgSize, imgSize);
tile.frame = frame;
if(col<[tempArray count])
[self addContentForTile:tile:row:col];
else tile.backgroundColor = [UIColor clearColor];
[songsContainer addSubview:tile];
}
}
}
firstVisibleRow = firstNeededRow+1; firstVisibleColumn = firstNeededCol+1;
lastVisibleRow = lastNeededRow; lastVisibleColumn = lastNeededCol;
What you have to do is create your scrollview however you want. You have to decide whether you want a grid layout, or linear layout. In addition, if grid, do you want it locked to the horizontal bounds, locked to vertical, so it scrolls either vertical or horizontal, respectively.
Once you have that sorted out, then what I recommend is taking the architecture similar to how a tableview functions. That is, create individual "cells" that will hold your thumbnails. Once you have these cells, you add them as subviews of your scrollview, at certain offsets (you need to do some math on the x/y planes).
Start with a scroll view and add each image in an UIImageView as a subview to the scrollview at a certain location.
One thing to have in mind is to only hold in memory the images that are currently shown and their immediate neighbours.