У меня есть UIImageView
внутри, UIScrollView
который я использую для масштабирования и прокрутки. Если изображение / содержимое прокрутки больше, чем прокрутка, все работает нормально. Однако, когда изображение становится меньше, чем в режиме прокрутки, оно остается в верхнем левом углу в режиме прокрутки. Я бы хотел, чтобы он был по центру, как приложение «Фото».
Есть идеи или примеры того, как сохранить содержимое по UIScrollView
центру, когда оно меньше?
Я работаю с iPhone 3.0.
Следующий код почти работает. Изображение вернется в верхний левый угол, если я ущипну его после достижения минимального уровня масштабирования.
- (void)loadView {
[super loadView];
// set up main scroll view
imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
[imageScrollView setBackgroundColor:[UIColor blackColor]];
[imageScrollView setDelegate:self];
[imageScrollView setBouncesZoom:YES];
[[self view] addSubview:imageScrollView];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
[imageView setTag:ZOOM_VIEW_TAG];
[imageScrollView setContentSize:[imageView frame].size];
[imageScrollView addSubview:imageView];
CGSize imageSize = imageView.image.size;
[imageView release];
CGSize maxSize = imageScrollView.frame.size;
CGFloat widthRatio = maxSize.width / imageSize.width;
CGFloat heightRatio = maxSize.height / imageSize.height;
CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
[imageScrollView setMinimumZoomScale:initialZoom];
[imageScrollView setZoomScale:1];
float topInset = (maxSize.height - imageSize.height) / 2.0;
float sideInset = (maxSize.width - imageSize.width) / 2.0;
if (topInset < 0.0) topInset = 0.0;
if (sideInset < 0.0) sideInset = 0.0;
[imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}
/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
[scrollView setZoomScale:scale+0.01 animated:NO];
[scrollView setZoomScale:scale animated:NO];
// END Bug workaround
CGSize maxSize = imageScrollView.frame.size;
CGSize viewSize = view.frame.size;
float topInset = (maxSize.height - viewSize.height) / 2.0;
float sideInset = (maxSize.width - viewSize.width) / 2.0;
if (topInset < 0.0) topInset = 0.0;
if (sideInset < 0.0) sideInset = 0.0;
[imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
Ответы:
У меня очень простое решение! Все, что вам нужно, это обновить центр вашего подпредставления (изображения) при увеличении масштаба ScrollViewDelegate. Если увеличенное изображение меньше, чем scrollview, отрегулируйте subview. Center else center равно (0,0).
- (void)scrollViewDidZoom:(UIScrollView *)scrollView { UIView *subView = [scrollView.subviews objectAtIndex:0]; CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0); subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, scrollView.contentSize.height * 0.5 + offsetY); }
источник
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentInset.left - scrollView.contentInset.right - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom - scrollView.contentSize.height) * 0.5, 0.0);
zoomToRect:
. ЭтотcontentInset
подход работает лучше, если вам понадобится эта функциональность. См. Petersteinberger.com/blog/2013/how-to-center-uiscrollview для получения более подробной информации.Ответ @EvelynCordner оказался лучшим в моем приложении. Гораздо меньше кода, чем в других вариантах.
Вот версия Swift, если она кому-то нужна:
func scrollViewDidZoom(_ scrollView: UIScrollView) { let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0) let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0) scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0) }
источник
Хорошо, я боролся с этим в течение последних двух дней и, наконец, пришел к довольно надежному (пока ...) решению, я подумал, что должен поделиться им и избавить других от боли. :) Если вы обнаружите проблему с этим решением, пожалуйста, кричите!
Я в основном прошел через то, что есть у всех: поиск StackOverflow, форумов разработчиков Apple, просмотрел код для three20, ScrollingMadness, ScrollTestSuite и т. Д. Я попытался увеличить фрейм UIImageView, играя со смещением и / или вставками UIScrollView из ViewController и т. д., но ничего не работало отлично (как и все остальные).
Выспавшись на нем, я попробовал несколько альтернативных ракурсов:
С помощью этого подкласса метода UIScrollView я переопределяю мутатор contentOffset, поэтому он не устанавливает {0,0}, когда изображение масштабируется меньше, чем область просмотра, - вместо этого он устанавливает смещение таким образом, чтобы изображение оставалось центрированным в области просмотра. Пока вроде всегда работает. Я проверил это с широкими, высокими, крошечными и большими изображениями, и у меня нет проблемы «работает, но ущипнуть при минимальном увеличении не удается».
Я загрузил на github пример проекта, который использует это решение, вы можете найти его здесь: http://github.com/nyoron/NYOBetterZoom
источник
anOffset.y = -(scrollViewSize.height - zoomViewSize.height) / 2.0
наanOffset.y = (-(scrollViewSize.height - zoomViewSize.height) / 2.0) + 10;
Этот код должен работать в большинстве версий iOS (и был протестирован для работы в версиях 3.1 и выше).
Он основан на коде Apple WWDC для фотосколлера.
Добавьте приведенный ниже код в свой подкласс UIScrollView и замените tileContainerView представлением, содержащим ваше изображение или плитки:
- (void)layoutSubviews { [super layoutSubviews]; // center the image as it becomes smaller than the size of the screen CGSize boundsSize = self.bounds.size; CGRect frameToCenter = tileContainerView.frame; // center horizontally if (frameToCenter.size.width < boundsSize.width) frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2; else frameToCenter.origin.x = 0; // center vertically if (frameToCenter.size.height < boundsSize.height) frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2; else frameToCenter.origin.y = 0; tileContainerView.frame = frameToCenter; }
источник
UIScrollView
подход подкласса кажется предпочтительным: он вызывается, когда представление отображается в первый раз + непрерывно во время масштабирования (тогда какscrollViewDidZoom
вызывается только один раз для каждой прокрутки и после факта)Для решения, лучше подходящего для представлений прокрутки, в которых используется автоматическое размещение, используйте вставки содержимого представления прокрутки, а не обновляйте фреймы вложенных представлений представления прокрутки.
- (void)scrollViewDidZoom:(UIScrollView *)scrollView { CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0); self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f); }
источник
UIView
что находится внутриUIScrollview
в тот же центр (view.center = scrollview.center;
) , а затем вscrollViewDidZoom
сетеview.frame.origin
x
иy
на0
снова.dispatch_async
с основной очередью, вviewWillAppear
которой я вызываюscrollViewDidZoom:
свой основной вид прокрутки. Это заставляет представление отображаться с центрированными изображениями.viewDidLayoutSubviews
чтобы убедиться, что он правильно настроен, когда представление отображается в первый раз.В настоящее время я создаю подклассы
UIScrollView
и переопределяю,setContentOffset:
чтобы настроить смещение на основеcontentSize
. Он работает как с масштабированием, так и с программным масштабированием.@implementation HPCenteringScrollView - (void)setContentOffset:(CGPoint)contentOffset { const CGSize contentSize = self.contentSize; const CGSize scrollViewSize = self.bounds.size; if (contentSize.width < scrollViewSize.width) { contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0; } if (contentSize.height < scrollViewSize.height) { contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0; } [super setContentOffset:contentOffset]; } @end
Этот код не только краток и понятен, но и обеспечивает более плавное масштабирование, чем решение @Erdemus. Вы можете увидеть это в действии в демонстрации RMGallery .
источник
Я потратил день на борьбу с этой проблемой и в итоге реализовал scrollViewDidEndZooming: withView: atScale: следующим образом:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width; CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height; CGFloat viewWidth = view.frame.size.width; CGFloat viewHeight = view.frame.size.height; CGFloat x = 0; CGFloat y = 0; if(viewWidth < screenWidth) { x = screenWidth / 2; } if(viewHeight < screenHeight) { y = screenHeight / 2 ; } self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x); }
Это гарантирует, что когда изображение меньше экрана, вокруг него останется достаточно места, чтобы вы могли разместить его в нужном месте.
(при условии, что ваш UIScrollView содержит UIImageView для хранения изображения)
По сути, это проверяет, меньше ли ширина / высота вашего изображения, чем ширина / высота экрана, и если да, создайте вставку, равную половине ширины / высоты экрана (вы, вероятно, могли бы сделать это больше, если хотите, чтобы изображение было выходят за пределы экрана).
Обратите внимание: поскольку это метод UIScrollViewDelegate , не забудьте добавить его в объявление вашего контроллера представления, чтобы избежать проблем со сборкой.
источник
Apple выпустила видеоролики о сессиях WWDC 2010 года для всех участников программы для разработчиков iphone. Одна из обсуждаемых тем - как они создали приложение для фотографий !!! Они шаг за шагом создают очень похожее приложение и сделали весь код доступным бесплатно.
Он также не использует частный api. Я не могу поместить сюда какой-либо код из-за соглашения о неразглашении, но вот ссылка на загрузку образца кода. Возможно, вам потребуется войти в систему, чтобы получить доступ.
http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645
И вот ссылка на страницу iTunes WWDC:
http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj
источник
Если он
contentInset
больше ни для чего не нужен, его можно использовать для центрирования содержимого scrollview.class ContentCenteringScrollView: UIScrollView { override var bounds: CGRect { didSet { updateContentInset() } } override var contentSize: CGSize { didSet { updateContentInset() } } private func updateContentInset() { var top = CGFloat(0) var left = CGFloat(0) if contentSize.width < bounds.width { left = (bounds.width - contentSize.width) / 2 } if contentSize.height < bounds.height { top = (bounds.height - contentSize.height) / 2 } contentInset = UIEdgeInsets(top: top, left: left, bottom: top, right: left) } }
Преимущество этого подхода в том, что вы все еще можете использовать его
contentLayoutGuide
для размещения содержимого внутри scrollview.scrollView.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), imageView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), imageView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor) ])
или просто перетащите содержимое в Интерфейсный Разработчик Xcode.
источник
Хорошо, это решение работает для меня. У меня есть подкласс
UIScrollView
со ссылкой на то, чтоUIImageView
он отображает. При каждомUIScrollView
увеличении изменяется свойство contentSize. Именно в сеттере яUIImageView
соответствующим образом масштабирую, а также регулирую его центральное положение.-(void) setContentSize:(CGSize) size{ CGSize lSelfSize = self.frame.size; CGPoint mid; if(self.zoomScale >= self.minimumZoomScale){ CGSize lImageSize = cachedImageView.initialSize; float newHeight = lImageSize.height * self.zoomScale; if (newHeight < lSelfSize.height ) { newHeight = lSelfSize.height; } size.height = newHeight; float newWidth = lImageSize.width * self.zoomScale; if (newWidth < lSelfSize.width ) { newWidth = lSelfSize.width; } size.width = newWidth; mid = CGPointMake(size.width/2, size.height/2); } else { mid = CGPointMake(lSelfSize.width/2, lSelfSize.height/2); } cachedImageView.center = mid; [super setContentSize:size]; [self printLocations]; NSLog(@"zoom %f setting size %f x %f",self.zoomScale,size.width,size.height); }
Всегда я устанавливаю изображение на
UIScrollView
Я изменяю его размер. ВUIScrollView
scrollview также есть созданный мной пользовательский класс.-(void) resetSize{ if (!scrollView){//scroll view is view containing imageview return; } CGSize lSize = scrollView.frame.size; CGSize lSelfSize = self.image.size; float lWidth = lSize.width/lSelfSize.width; float lHeight = lSize.height/lSelfSize.height; // choose minimum scale so image width fits screen float factor = (lWidth<lHeight)?lWidth:lHeight; initialSize.height = lSelfSize.height * factor; initialSize.width = lSelfSize.width * factor; [scrollView setContentSize:lSize]; [scrollView setContentOffset:CGPointZero]; scrollView.userInteractionEnabled = YES; }
С помощью этих двух методов я могу получить представление, которое ведет себя так же, как приложение для фотографий.
источник
Я сделал это так, чтобы добавить в иерархию дополнительное представление:
UIScrollView -> UIView -> UIImageView
Задайте для вас
UIView
такое же соотношение сторон, как и у васUIScrollView
, и сосредоточьтесьUIImageView
на нем.источник
Я знаю, что некоторые ответы выше верны, но я просто хочу дать свой ответ с некоторыми пояснениями, комментарии помогут вам понять, почему нам это нравится.
Когда я загружаю scrollView в первый раз, я пишу следующий код, чтобы сделать его по центру, обратите внимание, что мы
contentOffset
сначала устанавливаем , а затемcontentInset
scrollView.maximumZoomScale = 8 scrollView.minimumZoomScale = 1 // set vContent frame vContent.frame = CGRect(x: 0, y: 0 , width: vContentWidth, height: vContentWidth) // set scrollView.contentSize scrollView.contentSize = vContent.frame.size //on the X direction, if contentSize.width > scrollView.bounds.with, move scrollView from 0 to offsetX to make it center(using `scrollView.contentOffset`) // if not, don't need to set offset, but we need to set contentInset to make it center.(using `scrollView.contentInset`) // so does the Y direction. let offsetX = max((scrollView.contentSize.width - scrollView.bounds.width) * 0.5, 0) let offsetY = max((scrollView.contentSize.height - scrollView.bounds.height) * 0.5, 0) scrollView.contentOffset = CGPoint(x: offsetX, y: offsetY) let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0) let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0) scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)
Затем, когда я зажимаю vContent, я пишу следующий код, чтобы сделать его центром.
func scrollViewDidZoom(_ scrollView: UIScrollView) { //we just need to ensure that the content is in the center when the contentSize is less than scrollView.size. let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0) let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0) scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0) }
источник
Вы можете наблюдать за
contentSize
свойством UIScrollView (используя наблюдение за значением ключа или подобное) и автоматически настраиватьcontentInset
каждый раз, когдаcontentSize
изменения будут меньше размера прокрутки.источник
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
(описано на developer.apple.com/iphone/library/documentation/UIKit/… ), но я бы предпочел наблюдать,contentSize
потому что в противном случае вы откажетесь от новой шкалы масштабирования и все равно найдете ее в представлении. (Если только вы не сможете провести отличную математику, основанную на относительных размерах ваших просмотров.)Один из элегантных способов центрировать содержимое
UISCrollView
- это.Добавьте одного наблюдателя в contentSize вашего
UIScrollView
, чтобы этот метод вызывался каждый раз при изменении содержимого ...[myScrollView addObserver:delegate forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
Теперь о вашем методе наблюдателя:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Correct Object Class. UIScrollView *pointer = object; // Calculate Center. CGFloat topCorrect = ([pointer bounds].size.height - [pointer viewWithTag:100].bounds.size.height * [pointer zoomScale]) / 2.0 ; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); topCorrect = topCorrect - ( pointer.frame.origin.y - imageGallery.frame.origin.y ); // Apply Correct Center. pointer.center = CGPointMake(pointer.center.x, pointer.center.y + topCorrect ); }
Вам следует изменить
[pointer viewWithTag:100]
. Замените просмотром содержимогоUIView
.imageGallery
указатель на размер вашего окна.Это будет корректировать центр содержимого каждый раз, когда его размер изменится.
ПРИМЕЧАНИЕ . Единственный способ, при котором это содержимое работает не очень хорошо, - это использование стандартных функций масштабирования
UIScrollView
.источник
Это мое решение этой проблемы, которое отлично работает для любого вида внутри scrollview.
-(void)scrollViewDidZoom:(__unused UIScrollView *)scrollView { CGFloat top; CGFloat left; CGFloat bottom; CGFloat right; if (_scrollView.contentSize.width < scrollView.bounds.size.width) { DDLogInfo(@"contentSize %@",NSStringFromCGSize(_scrollView.contentSize)); CGFloat width = (_scrollView.bounds.size.width-_scrollView.contentSize.width)/2.0; left = width; right = width; }else { left = kInset; right = kInset; } if (_scrollView.contentSize.height < scrollView.bounds.size.height) { CGFloat height = (_scrollView.bounds.size.height-_scrollView.contentSize.height)/2.0; top = height; bottom = height; }else { top = kInset; right = kInset; } _scrollView.contentInset = UIEdgeInsetsMake(top, left, bottom, right); if ([self.tiledScrollViewDelegate respondsToSelector:@selector(tiledScrollViewDidZoom:)]) { [self.tiledScrollViewDelegate tiledScrollViewDidZoom:self]; } }
источник
Здесь есть масса решений, но я бы рискнул поставить здесь свое. Это хорошо по двум причинам: это не мешает масштабированию, как и обновление кадра просмотра изображения в процессе, а также уважает оригинальные вставки прокрутки (скажем, определенные в xib или раскадровке для изящной обработки полупрозрачных панелей инструментов и т. Д.) .
Сначала определите небольшого помощника:
CGSize CGSizeWithAspectFit(CGSize containerSize, CGSize contentSize) { CGFloat containerAspect = containerSize.width / containerSize.height, contentAspect = contentSize.width / contentSize.height; CGFloat scale = containerAspect > contentAspect ? containerSize.height / contentSize.height : containerSize.width / contentSize.width; return CGSizeMake(contentSize.width * scale, contentSize.height * scale); }
Чтобы сохранить исходные вставки, определенное поле:
UIEdgeInsets originalScrollViewInsets;
И где-нибудь в viewDidLoad залейте:
originalScrollViewInsets = self.scrollView.contentInset;
Чтобы поместить UIImageView в UIScrollView (при условии, что сам UIImage находится в loadedImage var):
CGSize containerSize = self.scrollView.bounds.size; containerSize.height -= originalScrollViewInsets.top + originalScrollViewInsets.bottom; containerSize.width -= originalScrollViewInsets.left + originalScrollViewInsets.right; CGSize contentSize = CGSizeWithAspectFit(containerSize, loadedImage.size); UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRect) { CGPointZero, contentSize }]; imageView.autoresizingMask = UIViewAutoresizingNone; imageView.contentMode = UIViewContentModeScaleAspectFit; imageView.image = loadedImage; [self.scrollView addSubview:imageView]; self.scrollView.contentSize = contentSize; [self centerImageViewInScrollView];
scrollViewDidZoom: из UIScrollViewDelegate для этого представления прокрутки:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView { if (scrollView == self.scrollView) { [self centerImageViewInScrollView]; } }
Наконец, само центрирование:
- (void)centerImageViewInScrollView { CGFloat excessiveWidth = MAX(0.0, self.scrollView.bounds.size.width - self.scrollView.contentSize.width), excessiveHeight = MAX(0.0, self.scrollView.bounds.size.height - self.scrollView.contentSize.height), insetX = excessiveWidth / 2.0, insetY = excessiveHeight / 2.0; self.scrollView.contentInset = UIEdgeInsetsMake( MAX(insetY, originalScrollViewInsets.top), MAX(insetX, originalScrollViewInsets.left), MAX(insetY, originalScrollViewInsets.bottom), MAX(insetX, originalScrollViewInsets.right) ); }
Я еще не тестировал изменение ориентации (т.е. правильную реакцию на изменение размера самого UIScrollView), но исправить это должно быть относительно легко.
источник
Вы обнаружите, что решение, опубликованное Erdemus, действительно работает, но ... В некоторых случаях метод scrollViewDidZoom не вызывается и ваше изображение застревает в верхнем левом углу. Простое решение - явно вызвать метод при первоначальном отображении изображения, например:
[self scrollViewDidZoom: scrollView];
Во многих случаях вы можете вызывать этот метод дважды, но это более чистое решение, чем некоторые другие ответы в этой теме.
источник
Пример скроллера фотографий Apple делает именно то, что вы ищете. Поместите это в свой подкласс UIScrollView и измените _zoomView на свой UIImageView.
-(void)layoutSubviews{ [super layoutSubviews]; // center the zoom view as it becomes smaller than the size of the screen CGSize boundsSize = self.bounds.size; CGRect frameToCenter = self.imageView.frame; // center horizontally if (frameToCenter.size.width < boundsSize.width){ frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2; }else{ frameToCenter.origin.x = 0; } // center vertically if (frameToCenter.size.height < boundsSize.height){ frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2; }else{ frameToCenter.origin.y = 0; } self.imageView.frame = frameToCenter; }
Пример кода скроллера фотографий Apple
источник
Чтобы анимация плавно отображалась, установите
self.scrollview.bouncesZoom = NO;
и используйте эту функцию (поиск центра с помощью метода в этом ответе )
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { [UIView animateWithDuration:0.2 animations:^{ float offsetX = MAX((scrollView.bounds.size.width-scrollView.contentSize.width)/2, 0); float offsetY = MAX((scrollView.bounds.size.height-scrollView.contentSize.height)/2, 0); self.imageCoverView.center = CGPointMake(scrollView.contentSize.width*0.5+offsetX, scrollView.contentSize.height*0.5+offsetY); }]; }
Это создает эффект подпрыгивания, но заранее не предполагает никаких резких движений.
источник
Просто одобренный ответ быстро, но без подкласса с использованием делегата
func centerScrollViewContents(scrollView: UIScrollView) { let contentSize = scrollView.contentSize let scrollViewSize = scrollView.frame.size; var contentOffset = scrollView.contentOffset; if (contentSize.width < scrollViewSize.width) { contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0 } if (contentSize.height < scrollViewSize.height) { contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0 } scrollView.setContentOffset(contentOffset, animated: false) } // UIScrollViewDelegate func scrollViewDidZoom(scrollView: UIScrollView) { centerScrollViewContents(scrollView) }
источник
Если ваш внутренний imageView имеет начальную конкретную ширину (например, 300), и вы просто хотите центрировать его ширину только при масштабировании меньше его начальной ширины, это также может вам помочь.
func scrollViewDidZoom(scrollView: UIScrollView){ if imageView.frame.size.width < 300{ imageView.center.x = self.view.frame.width/2 } }
источник
Вот как я сейчас работаю. Это лучше, но все же не идеально. Попробуйте установить:
myScrollView.bouncesZoom = YES;
чтобы устранить проблему с нецентрированием обзора при на
minZoomScale
.- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGSize screenSize = [[self view] bounds].size;//[[UIScreen mainScreen] bounds].size;// CGSize photoSize = [yourImage size]; CGFloat topInset = (screenSize.height - photoSize.height * [myScrollView zoomScale]) / 2.0; CGFloat sideInset = (screenSize.width - photoSize.width * [myScrollView zoomScale]) / 2.0; if (topInset < 0.0) { topInset = 0.0; } if (sideInset < 0.0) { sideInset = 0.0; } [myScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)]; ApplicationDelegate *appDelegate = (ApplicationDelegate *)[[UIApplication sharedApplication] delegate]; CGFloat scrollViewHeight; //Used later to calculate the height of the scrollView if (appDelegate.navigationController.navigationBar.hidden == YES) //If the NavBar is Hidden, set scrollViewHeight to 480 { scrollViewHeight = 480; } if (appDelegate.navigationController.navigationBar.hidden == NO) //If the NavBar not Hidden, set scrollViewHeight to 360 { scrollViewHeight = 368; } imageView.frame = CGRectMake(0, 0, CGImageGetWidth(yourImage)* [myScrollView zoomScale], CGImageGetHeight(yourImage)* [myScrollView zoomScale]); [imageView setContentMode:UIViewContentModeCenter]; }
Кроме того, я делаю следующее, чтобы изображение не склеивалось сбоку после уменьшения масштаба.
- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { myScrollView.frame = CGRectMake(0, 0, 320, 420); //put the correct parameters for your scroll view width and height above }
источник
Ладно, думаю, я нашел неплохое решение этой проблемы. Хитрость заключается в том, чтобы постоянно подправлять
imageView's
раму. Я считаю, что это работает намного лучше, чем постоянно корректироватьcontentInsets
илиcontentOffSets
. Мне пришлось добавить немного дополнительного кода для размещения как портретных, так и альбомных изображений.Вот код:
- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { CGSize screenSize = [[self view] bounds].size; if (myScrollView.zoomScale <= initialZoom +0.01) //This resolves a problem with the code not working correctly when zooming all the way out. { imageView.frame = [[self view] bounds]; [myScrollView setZoomScale:myScrollView.zoomScale +0.01]; } if (myScrollView.zoomScale > initialZoom) { if (CGImageGetWidth(temporaryImage.CGImage) > CGImageGetHeight(temporaryImage.CGImage)) //If the image is wider than tall, do the following... { if (screenSize.height >= CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is greater than the zoomed height of the image do the following... { imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), 368); } if (screenSize.height < CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is less than the zoomed height of the image do the following... { imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]); } } if (CGImageGetWidth(temporaryImage.CGImage) < CGImageGetHeight(temporaryImage.CGImage)) //If the image is taller than wide, do the following... { CGFloat portraitHeight; if (CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale] < 368) { portraitHeight = 368;} else {portraitHeight = CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale];} if (screenSize.width >= CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is greater than the zoomed width of the image do the following... { imageView.frame = CGRectMake(0, 0, 320, portraitHeight); } if (screenSize.width < CGImageGetWidth (temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is less than the zoomed width of the image do the following... { imageView.frame = CGRectMake(0, 0, CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale], portraitHeight); } } [myScrollView setZoomScale:myScrollView.zoomScale -0.01]; }
источник
Просто отключите разбиение на страницы, и все будет нормально:
scrollview.pagingEnabled = NO;
источник
У меня была точно такая же проблема. Вот как я решил
Этот код должен вызываться в результате
scrollView:DidScroll:
CGFloat imageHeight = self.imageView.frame.size.width * self.imageView.image.size.height / self.imageView.image.size.width; BOOL imageSmallerThanContent = (imageHeight < self.scrollview.frame.size.height) ? YES : NO; CGFloat topOffset = (self.imageView.frame.size.height - imageHeight) / 2; // If image is not large enough setup content offset in a way that image is centered and not vertically scrollable if (imageSmallerThanContent) { topOffset = topOffset - ((self.scrollview.frame.size.height - imageHeight)/2); } self.scrollview.contentInset = UIEdgeInsetsMake(topOffset * -1, 0, topOffset * -1, 0);
источник
Хотя вопрос немного устарел, но проблема все еще существует. Я решил это в Xcode 7 , установив ограничение вертикального пространства для самого верхнего элемента (в данном случае
topLabel
) на superViews (thescrollView
) top an,IBOutlet
а затем пересчитав его константу каждый раз, когда содержимое изменяется в зависимости от высоты subviews scrollView (topLabel
иbottomLabel
).class MyViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var topLabel: UILabel! @IBOutlet weak var bottomLabel: UILabel! @IBOutlet weak var toTopConstraint: NSLayoutConstraint! override func viewDidLayoutSubviews() { let heightOfScrollViewContents = (topLabel.frame.origin.y + topLabel.frame.size.height - bottomLabel.frame.origin.y) // In my case abs() delivers the perfect result, but you could also check if the heightOfScrollViewContents is greater than 0. toTopConstraint.constant = abs((scrollView.frame.height - heightOfScrollViewContents) / 2) } func refreshContents() { // Set the label's text … self.view.layoutIfNeeded() } }
источник