As shown in the image, my requirement is to avoid displaying half cell while scrolling....if i scroll more than half a height of cell and released, the cell should be completed scrolled to top..it is somewhat similar to setPagingEnabled concept...I want to implement same concept for each row.. Thanks in advance
Override
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
in UICollectionViewlayout. The return value is the offset you want to use instead of the one the user scrolled to. Assuming your row's height is 100 and the current proposedContentOffset is (0,80) then you would want to return (0,100) which would then scroll your collection view 20 more points. At that point, your previous row's cells won't be visible anymore.
(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGFloat proposedOffset = targetContentOffset->y;
CGFloat heightOfPage =65;
CGFloat heightOfSpacing = 5.0f;
CGFloat numOfPage = lround(proposedOffset / (heightOfPage + heightOfSpacing));
CGFloat newY = numOfPage * (heightOfPage + heightOfSpacing);
// if the calculated y is bigger then the maximum possible y we adjust accordingly
CGFloat contentHeight = _categoryCollectionView.contentSize.height;
CGFloat collectionViewHeight = _categoryCollectionView.bounds.size.height;
CGFloat maxY = contentHeight - collectionViewHeight;
if (newY > maxY)
{
newY = maxY;
}
targetContentOffset->y = (newY);
}
Related
I'm using a UIPinchGestureRecognizer to adjust the width (not the height) of a view in a UIScrollView. It works with the pinch gesture's scale property, but the contentOffset of the scrollview doesn't change, so the view always increases on the right. This looks a bit better if I scale the contentOffset along with the width, since then the view increases from the left-most side of the screen.
The problem is that the location of the pinch is ignored - so it always appears that a pinch is on the left side of the screen.
I need to somehow factor in the location of the pinch to the contentOffset adjustment, so that the offset can be adjusted to keep the content at the pinch point to be in the same place.
Note: I cannot use built-in UIScrollView pinch-zoom gesture as I only want the zoom to be one dimension, horizontal. Also, I cannot use transforms on the UIView as I need to use the UIScrollView.
I was pinch zooming a graph, so the pinch adjusts the width constraint on the graph view.
Here is the pinch handler:
- (void) doPinch:(UIPinchGestureRecognizer*)pinch;
{
CGFloat width = self.graphWidthConstraint.constant;
CGFloat idealWidth = 1500;
CGFloat currentScale = width / idealWidth;
CGFloat scale = currentScale - (1.0 - pinch.scale);
CGFloat minScale = 0.5;
CGFloat maxScale = 3.0;
scale = MIN(scale, maxScale);
scale = MAX(scale, minScale);
CGPoint locationScrollview = [pinch locationInView:self.graphScrollView];
CGFloat pinchXNormalized = locationScrollview.x / width;
CGPoint locationView = [pinch locationInView:self.view];
// resize
CGFloat newWidth = idealWidth * scale;
self.graphWidthConstraint.constant = newWidth;
// set offset to position point under touch
CGFloat pinchXScaled = newWidth * pinchXNormalized;
CGFloat x = pinchXScaled - locationView.x;
self.graphScrollView.contentOffset = CGPointMake(x, 0);
pinch.scale = 1;
}
I'm using collection view to view my rss feed data. I am using -(void)scrollViewDidScroll:(UIScrollView *)scrollView to detect the view go to bottom & load more rss feeds.
But I want call the scrollViewDidScroll method not in the bottom of my view, but just 2 or 3 index items before the bottom. How can I edit my scrollViewDidScroll method to perform this.
You can do something like this:
-(void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = -2 * [your item height];
if(y > h + reload_distance) {
[self.delegate loadMoreDataHere];
}
}
I'm attempting a parallax effect with a UIScrollView whereby there will be an UIImageView and UILabel per 'page', and as the user scrolls through the pages, the UIImageView will disappear off of the current page at a faster rate than the UILabel, while the next UIImageView and UILabel appear.
My code is working perfectly when on the first page as the amplifier is not taken into account, thus not working on any other page; the UIImageView is not centered horizontally within the page, which it should be.
Please can you tell me where I'm going wrong?
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
NSInteger currentPage = floorf(offset.x / CGRectGetWidth(self.view.frame));
if (currentPage >= 0 && currentPage < CINNumberOfWalkthroughs) {
NSInteger lastPage = self.pageControl.currentPage;
self.pageControl.currentPage = currentPage;
float imageViewAmplifier = offset.x / scrollView.contentSize.width;
UIImageView *imageView = self.imageViews[currentPage];
float w = CGRectGetWidth(self.view.frame);
float a = CGRectGetWidth(imageView.frame);
imageView.center = CGPointMake((currentPage * w) + ((w + a) / 2) + (offset.x * imageViewAmplifier), imageView.center.y);
}
}
I used the following calculation to determine the center position of each of the UIImageViews, and then add a portion of the offset to add the moving effect.
I managed to fix the issue by simplifying things - I have the tendency to overcomplicate everything which made this task a lot more difficult than it needed to be.
Instead of working out the current center of the imageView and performing the translation by transforming the center point, I used a simple CGAffineTransformMakeTranslation() which effectively deals with all of this for me. All I had to do from there was work out the actual offset from the origin of the page, and then fix the amplifier, which was corrected by working out the ration between the actual offset and the content size of the scroll view. My working solution can be found below:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
NSInteger currentPage = floorf(offset.x / CGRectGetWidth(self.view.frame));
if (currentPage >= 0 && currentPage < CINNumberOfWalkthroughs) {
self.pageControl.currentPage = currentPage;
UIImageView *imageView = self.imageViews[currentPage];
float multiplier = -1 * (offset.x / scrollView.contentSize.width);
float w = CGRectGetWidth(self.view.frame);
imageView.transform = CGAffineTransformMakeTranslation((offset.x - currentPage * w) * multiplier, 0);
UILabel *instructionLabel = self.instructionLabels[currentPage];
instructionLabel.transform = CGAffineTransformMakeTranslation((offset.x - currentPage * w) * (multiplier - 0.4), 0);
}
}
I have a custom horizontal collection view that has 1 row and I want to add pull to refresh functionality which, by default, appears above my row of cells. I would like the user to be able to pull the collection view from left to right to activate the UIRefreshControl. Any ideas?
Thanks in advance.
Basically the response above tells you how to do in Objective-C a load more in a UICollectionView. However, I believe the question was how to do pull to refresh horizontally on that component.
I don't think you can add a UIRefreshControl horizontally but taking into consideration the previous code and making a conversion to Swift I came up with the following one
DON'T FORGET TO SET YOUR UICollectionView bounce property TO TRUE
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset
let inset = scrollView.contentInset
let y: CGFloat = offset.x - inset.left
let reload_distance: CGFloat = -75
if y < reload_distance{
scrollView.bounces = false
UIView.animateWithDuration(0.3, animations: { () -> Void in
scrollView.setContentOffset(CGPointMake(0, 0), animated: false)
}, completion: { (Bool) -> Void in
scrollView.bounces = true
})
}
}
Also and in order to avoid issues with the continuous pulling I added some code to remove the bouncing temporarily, animate de scroll back to the right and then enabling the bouncing again. That will give you the same effect as the UIRefreshControl.
Finally, if you want to have a loading icon my suggestion is to add it behind the controller so when you pull you can see it behind
Just adding the Obj-C version of Julio Bailon's answer, which works for pulling the collectionView from its Top i.e. Left to Right
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.x - inset.left;
float h = size.width;
float reload_distance = -75; //distance for which you want to load more
if(y < reload_distance) {
// write your code getting the more data
NSLog(#"load more rows");
}
For this you need to implement the UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.x + bounds.size.width - inset.right;
float h = size.width;
float reload_distance = 75; //distance for which you want to load more
if(y > h + reload_distance) {
// write your code getting the more data
NSLog(#"load more rows");
}
}
I have a UICollectionView with paging enabled and the individual views that are moving horizontally are not getting centered properly.
I have made sure that the views are the same width as the screen.
Any pointers on how to force the UICollectionView to page the views horizontally?
You have to make sure that your section inset + line spacing + cell width all equals exactly the bounds of the CollectionView. I use the following method on a custom UICollectionViewFlowLayout sublcass:
- (void)adjustSpacingForBounds:(CGRect)newBounds {
NSInteger count = newBounds.size.width / self.itemSize.width - 1;
CGFloat spacing = (newBounds.size.width - (self.itemSize.width * count)) / count;
self.minimumLineSpacing = spacing;
UIEdgeInsets insets = self.sectionInset;
insets.left = spacing/2.0f;
self.sectionInset = insets;
}
You have to remember that UICollectionView is just a sublcass of UIScrollView so it doesn't know how you want your pagination to work. Before UICollectionView you would have to handle all of this math when laying out your subviews of the UIScrollView.
Override the method:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
*targetContentOffset = scrollView.contentOffset; // set acceleration to 0.0
float pageWidth = (float)self.articlesCollectionView.bounds.size.width;
int minSpace = 10;
int cellToSwipe = (scrollView.contentOffset.x)/(pageWidth + minSpace) + 0.5; // cell width + min spacing for lines
if (cellToSwipe < 0) {
cellToSwipe = 0;
} else if (cellToSwipe >= self.articles.count) {
cellToSwipe = self.articles.count - 1;
}
[self.articlesCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:cellToSwipe inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}