I have a scroll view with 11 images . I want when the last image(11) it's showed , so the scroll view is ended , to get back to the first image. Any ideea how can i do this ?
I was tryed with :
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView{
return YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.scrollView.contentOffset = CGPointMake(0,0);
}
but i don;t have the result wanted.
Firstly you need to check whether content in the scroll view ended or not. If its ended set its content offset to 0.
CGFloat bottomInset = scrollView.contentInset.bottom;
CGFloat bottomEdge = scrollView.contentOffset.y + scrollView.frame.size.height - bottomInset;
if (bottomEdge == scrollView.contentSize.height) {
// Scroll view is scrolled to bottom
[self.scrollView setContentOffset:CGPointMake(0, self.scrollView.contentOffset.y)];
}
use these to check you scrolled to bottom or not..i hop it helps
-(void)scrollViewDidScroll: (UIScrollView*)scrollView
{
float scrollViewHeight = self.scrollView.frame.size.height;
float scrollContentSizeHeight = self.scrollView.contentSize.height;
float scrollOffset = self.scrollView.contentOffset.y;
if (scrollOffset == 0)
{
// then we are at the top
}
else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
// then we are at the end
[self.scrollView setContentOffset:CGPointMake(0, self.scrollView.contentOffset.y)];
}
}
Related
I have setup scrollView delegate and implemented the logic into scrollViewDidScroll protocol method as follows, it never goes into the second condition. I wonder what I am missing?
-(void)scrollViewDidScroll: (UIScrollView*)scrollView
{
float scrollViewHeight = scrollView.frame.size.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 0)
{
// then we are at the top
}
else if (scrollOffset + scrollViewHeight == 700.0)
{
// never calls here
[self loadSecondFromURL];
}
}
I think scrollOffset is zero so, its entering only first loop. I changed the code like this and it works fine for me
if (scrollOffset == 0)
{
// then we are at the top
}
if (scrollOffset + scrollViewHeight == 700.0)
{
// never calls here
[self loadSecondFromURL];
}
I am adding a UIView to the end of my UIScrollView as follows -
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 0)
{
}
else if (scrollOffset + scrollViewHeight >= scrollContentSizeHeight)
{
UIView *paintView=[[UIView alloc]initWithFrame:CGRectMake(0, scrollOffset + scrollViewHeight + 20, self.view.frame.size.width, 200)];
[paintView setBackgroundColor:[UIColor yellowColor]];
[self.containerScrollView addSubview:paintView];
}
}
This added the view at the end, but I am not able to scroll that view. How do I enable scrolling to the newly added view as well?
You can set scrollview's contentInset
scrollView.contentInset = UIEdgeInsetsMake(0,0,extensionHeight,0);
but you probably don't wanna add subview in - (void)scrollViewDidScroll:(UIScrollView *)scrollView ,because this function would be invoked many times, your subview would be created many times.
If you have to add subview in it, I suggest you create a property paintView, and check if it is nil, if it is, create it, if not, just don't do anything
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 0)
{
}
else if (scrollOffset + scrollViewHeight >= scrollContentSizeHeight)
{
scrollView.contentInset = UIEdgeInsetsMake(0,0,extensionHeight,0);
if (!_paintView) {
_paintView=[[UIView alloc]initWithFrame:CGRectMake(0, scrollOffset + scrollViewHeight + 20, self.view.frame.size.width, 200)];
[_paintView setBackgroundColor:[UIColor yellowColor]];
[self.containerScrollView addSubview:_paintView];
}
}
}
I have the following code in place to grab the event when a UIScrollView reaches then end of its content view:
- (void) scrollViewDidEndDecelerating:(UIScrollView *) scrollView
{
float currentEndPoint = scrollView.contentOffset.y + scrollView.frame.size.height;
// CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height);
// [scrollView setContentOffset:bottomOffset animated:NO];
if (currentEndPoint >= scrollView.contentSize.height)
{
// We are at the bottom
I notice that when I scroll to the bottom it hits it and bounces back up.
If I add this:
CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height);
[scrollView setContentOffset:bottomOffset animated:NO];
then the scroll goes back to the bottom.
Is there any way for it to stay at the bottom without the "bounce back up" as in once it hits the bottom, stop it from moving.
Thanks.
You should uncheck the bounce property of scrollview. Check the screenshot!
I didn't understand much what you mean, what I got that you want to stop scrolling of table view when it reaches to bottom. So here is the process:-
- (void) scrollViewDidEndDecelerating:(UIScrollView *) scrollView
{
float currentEndPoint = scrollView.contentOffset.y + scrollView.frame.size.height;
if (currentEndPoint >= scrollView.contentSize.height)
{
CGPoint offset = scrollView.contentOffset;
offset.x -= 1.0;
offset.y -= 1.0;
[scrollView setContentOffset:offset animated:NO];
offset.x += 1.0;
offset.y += 1.0;
[scrollView setContentOffset:offset animated:NO];
}
}
bounce back problem, u can resolve by
var progressScrollView = UIScrollView()
progressScrollView.bounces = false
I'm implementing pinch to zoom of an image using UIImageView inside a UIScrollView. The scroll view doesn't take full screen size. I made the background of the scrollview green. I then pinch-zoomed in the image.
The problem is that when I scroll the zoomed image it's not centered correctly in the scroll view and has some space to the right and bottom. If I try to scroll to the top or left the scroll view doesn't allow to scroll to the end of the image by similar amount of space. The amount of space seems to be related on how I move my fingers when doing the pinch gesture.
Here is a screenshot:
Here is my code:
// On viewWillAppear:
- (void)loadMyImage
{
self.myImage.contentMode = UIViewContentModeScaleAspectFit;
self.myImage.image = self.originalImage; // an UIImage loaded from gallery
self.myImage.bounds = CGRectMake(0, 0, self.originalImage.size.width, self.originalImage.size.height);
self.scrollView.bounds = CGRectMake(0,0,320,200);
self.scrollView.frame = CGRectMake(0,0,320,200);
self.scrollView.minimumZoomScale=1.0f;
self.scrollView.maximumZoomScale=2.0f;
self.scrollView.delegate = self;
self.myImage.userInteractionEnabled = YES;
}
// Scroll view zooming events:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{return self.myImage;}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.myImage.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.myImage.center = CGPointMake(
contentsFrame.origin.x + contentsFrame.size.width / 2.0f,
contentsFrame.origin.y + contentsFrame.size.height / 2.0f);
CGPoint offset = CGPointMake(
self.myImage.center.x - self.scrollView.bounds.size.width / 2.0f,
self.myImage.center.y - self.scrollView.bounds.size.height / 2.0f);
[UIView animateWithDuration:.25 animations:^{
[self.scrollView setContentOffset:offset];
}];
NSLog(#"Final state of frame: %g x %g (%g , %g) and center (%g , %g)",
self.myImage.frame.size.width, self.myImage.frame.size.height,
self.myImage.frame.origin.x, self.myImage.frame.origin.y,
self.myImage.center.x, self.myImage.center.y);
NSLog(#"offset: (%g , %g)",
self.scrollView.contentOffset.x, self.scrollView.contentOffset.y);
NSLog(#"Inset left %g , right %g , top %g , bottom %g",
self.scrollView.contentInset.left,
self.scrollView.contentInset.right,
self.scrollView.contentInset.top,
self.scrollView.contentInset.bottom);
NSLog(#"==================================");
}
And here is my output:
Initial state of bounds: 320 x 200
of frame: 320 x 200 (0 , 0) and center (160 , 100)
offset: (0 , 0)
==================================
Final state of frame: 640 x 400 (0 , 0) and center (320 , 200)
offset: (160 , 100)
Inset left 0 , right 0 , top 0 , bottom 0
==================================
I tried setting automaticallyAdjustsScrollViewInsets to NO, but my view controller doesn't respond to the selector. And besides, the insets report 0-s.
Any solution or work-around would be great. Any idea or possible explanation for this behavior would also be appreciated.
I suggest you to use the following library in Github:
https://github.com/akhiljayaram/PJImageZoomView
Don't forget to add
self.automaticallyAdjustsScrollViewInsets = NO;
in your viewcontroller.
Hope this helps! :)
My question has to do with a custom form of paging which I am trying to do with a scroller, and this is easier to visualise if you first consider the type of scroll view implemented in a slot machine.
So say my UIScrollView has a width of 100 pixels. Assume it contains 3 inner views, each with a width of 30 pixels, such that they are separated by a width of 3 pixels. The type of paging which I would like to achieve, is such that each page is one of my views (30 pixels), and not the whole width of the scroll view.
I know that usually, if the view takes up the whole width of the scroll view, and paging is enabled then everything works. However, in my custom paging, I also want surrounding views in the scroll view to be visible as well.
How would I do this?
I just did this for another project. What you need to do is to place the UIScrollView into a custom implementation of UIView. I created a class for this called ExtendedHitAreaViewController. The ExtendedHitAreaView overrides the hitTest function to return its first child object, which will be your scroll view.
Your scroll view should be the page size you want, i.e., 30px with clipsToBounds = NO.
The extended hit area view should be the full size of the area you want to be visible, with clipsToBounds = YES.
Add the scroll view as a subview to the extended hit area view, then add the extended hit area view to your viewcontroller's view.
#implementation ExtendedHitAreaViewContainer
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
if ([[self subviews] count] > 0) {
//force return of first child, if exists
return [[self subviews] objectAtIndex:0];
} else {
return self;
}
}
return nil;
}
#end
Since iOS 5 there is this delegate method: - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset.
So you can do something like this:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint
*)targetContentOffset {
if (scrollView == self.scrollView) {
CGFloat x = targetContentOffset->x;
x = roundf(x / 30.0f) * 30.0f;
targetContentOffset->x = x;
}
}
For higher velocities you might want to adjust your targetContentOffset a bit different if you want a more snappy feeling.
I had the same problem and this worked great for me, tested on iOS 8.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSInteger index = lrint(targetContentOffset->x/_pageWidth);
NSInteger currentPage = lrint(scrollView.contentOffset.x/_pageWidth);
if(index == currentPage) {
if(velocity.x > 0)
index++;
else if(velocity.x < 0)
index--;
}
targetContentOffset->x = index * _pageWidth;
}
I had to check the velocity and always go to next/previous page if velocity was not zero, otherwise it would give non-animated jumps when doing very short and fast swipes.
Update: This seems to work even better:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat index = targetContentOffset->x/channelScrollWidth;
if(velocity.x > 0)
index = ceil(index);
else if(velocity.x < 0)
index = floor(index);
else
index = round(index);
targetContentOffset->x = index * channelScrollWidth;
}
Those are for a horizontal scrollview, use y instead of x for a vertical one.
I've been struggling to overcome this issue, and I found an almost perfect solution which is to ideal with and paging width you want.
I'd set scrollView.isPaging to false (meanwhile, it's false by default) from UIScrollView and set its delegate to UIScrollViewDelegate.
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Stop scrollView sliding:
targetContentOffset.pointee = scrollView.contentOffset
if scrollView == scrollView {
let maxIndex = slides.count - 1
let targetX: CGFloat = scrollView.contentOffset.x + velocity.x * 60.0
var targetIndex = Int(round(Double(targetX / (pageWidth + spacingWidth))))
var additionalWidth: CGFloat = 0
var isOverScrolled = false
if targetIndex <= 0 {
targetIndex = 0
} else {
// in case you want to make page to center of View
// by substract width with this additionalWidth
additionalWidth = 20
}
if targetIndex > maxIndex {
targetIndex = maxIndex
isOverScrolled = true
}
let velocityX = velocity.x
var newOffset = CGPoint(x: (CGFloat(targetIndex) * (self.pageWidth + self.spacingWidth)) - additionalWidth, y: 0)
if velocityX == 0 {
// when velocityX is 0, the jumping animation will occured
// if we don't set targetContentOffset.pointee to new offset
if !isOverScrolled && targetIndex == maxIndex {
newOffset.x = scrollView.contentSize.width - scrollView.frame.width
}
targetContentOffset.pointee = newOffset
}
// Damping equal 1 => no oscillations => decay animation:
UIView.animate(
withDuration: 0.3, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: velocityX,
options: .allowUserInteraction,
animations: {
scrollView.contentOffset = newOffset
scrollView.layoutIfNeeded()
}, completion: nil)
}
}
slides contains all page views that you have inserted to UIScrollView.
I have many different views inside scroll view with buttons and gesture recognisers.
#picciano's answer didn't work (scroll worked good but buttons and recognisers didn't get touches) for me so I found this solution:
class ExtendedHitAreaView : UIScrollView {
// Your insets
var hitAreaEdgeInset = UIEdgeInsets(top: 0, left: -20, bottom: 0, right: -20)
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let hitBounds = UIEdgeInsetsInsetRect(bounds, hitAreaEdgeInset)
return CGRectContainsPoint(hitBounds, point)
}
}
After a couple of days of researching and troubleshooting i came up with something that works for me!
First you need to subclass the view that the scrollview is in and override this method with the following:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
UIView* child = nil;
if ((child = [super hitTest:point withEvent:event]) == self)
return (UIView *)_calloutCell;
return child;
}
Then all the magic happens in the scrollview delegate methods
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
//_lastOffset is declared in the header file
//#property (nonatomic) CGPoint lastOffset;
_lastOffset = scrollView.contentOffset;
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint currentOffset = scrollView.contentOffset;
CGPoint newOffset = CGPointZero;
if (_lastOffset.x < currentOffset.x) {
// right to left
newOffset.x = _lastOffset.x + 298;
}
else {
// left to right
newOffset.x = _lastOffset.x - 298;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:0.0f];
targetContentOffset->x = newOffset.x;
[UIView commitAnimations];
}
You also need to set the scrollview's deceleration rate. I did it in ViewDidLoad
[self.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast];
alcides' solution works perfectly. i just enable / disable the scrolling of the scrollview, whenever i enter scrollviewDidEndDragging and scrollViewWillEndDragging. if the user scrolls several times before the paging animation is finished, the cells are slightly out of alignment.
so i have:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
scrollView.scrollEnabled = NO
CGPoint currentOffset = scrollView.contentOffset;
CGPoint newOffset = CGPointZero;
if (_lastOffset.x < currentOffset.x) {
// right to left
newOffset.x = _lastOffset.x + 298;
}
else {
// left to right
newOffset.x = _lastOffset.x - 298;
}
[UIView animateWithDuration:0.4 animations:^{
targetContentOffset.x = newOffset.x
}];
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView {
scrollView.scrollEnabled = YES
}