UIScrollView Zoom не работает с автозагрузкой
Масштабирование с помощью UIScrollView с использованием строго среды автозапуска не работает.
Это особенно неприятно, потому что примечания к выпуску iOS 6, безусловно, заставляют меня думать, что это должно произойти, когда написано о "Pure Auto Layout" здесь http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html
Я посмотрел слайды WWDC 2012 для сессий 202, 228 и 232 и не видел ответа для этого.
Единственный вопрос, который я видел в Интернете специально для этой проблемы, - это масштабирование UIScrollView с автоматической компоновкой, но оно не дает кода проблемы и там нет ответа.
Этот пользователь https://stackoverflow.com/users/341994/matt дал много замечательных ответов на вопросы автозапуска UIScrollView и даже связался с кодом на узле git, но я не смог найти что-нибудь, что ответит на этот вопрос.
Я попытался довести этот вопрос до абсолютного минимума, чтобы он дал понять.
Я создал новое одноразовое приложение с раскадрой и не вносил изменений в конструктор интерфейса.
Я добавил большой файл изображения в проект "pic.jpg".
SVFViewController.h
#import <UIKit/UIKit.h>
@interface SVFViewController : UIViewController <UIScrollViewDelegate>
@property (nonatomic) UIImageView *imageViewPointer;
@end
SVFViewController.m
#import "SVFViewController.h"
@interface SVFViewController ()
@end
@implementation SVFViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIScrollView *scrollView = [[UIScrollView alloc] init];
UIImageView *imageView = [[UIImageView alloc] init];
[imageView setImage:[UIImage imageNamed:@"pic.jpg"]];
[self.view addSubview:scrollView];
[scrollView addSubview:imageView];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.imageViewPointer = imageView;
scrollView.maximumZoomScale = 2;
scrollView.minimumZoomScale = .5;
scrollView.delegate = self;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView,imageView);
NSLog(@"Current views dictionary: %@", viewsDictionary);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
}
-(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return self.imageViewPointer;
}
@end
Обратите внимание, что я приложил все усилия, чтобы сделать это так же, как пример кода, приведенный в примечаниях к выпуску iOS 6, просто делая минимальный минимум для масштабирования.
Итак, проблема?
Когда вы запускаете это приложение и перемещаетесь в режиме прокрутки, все хорошо. Но при масштабировании проблема очевидна, изображение мерцает взад-вперед, и размещение изображения в пределах прокрутки становится более ошибочным с каждым увеличением.
Похоже, идет битва за смещение содержимого imageView, кажется, что с каждым "зумом" он устанавливает разные значения двумя разными вещами. (NSLog свойства смещения содержимого изображенияViewView подтверждает это).
Что я здесь делаю неправильно? Кто-нибудь знает, как реализовать масштабирование свойств в UIScrollView в чисто среде автозапуска. Есть ли пример этого где-нибудь там?
Пожалуйста, помогите.
Ответы
Ответ 1
Еще раз, перечитав примечания к выпуску iOS SDK 6.0, я обнаружил, что:
Обратите внимание, что вы можете заставить subview вид прокрутки отображаться как float (не прокручивать) по другому прокручиваемому контенту, создавая ограничения между представлением и представлением вне поддерева прокрутки, например, просмотр прокрутки.
Решение
Подключить подзону к внешнему виду. Другими словами, к представлению, в которое встроено scrollview.
И применяя ограничения следующим образом, у меня есть работа:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(width)]" options:0 metrics:@{@"width":@(self.imageViewPointer.image.size.width)} views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(height)]" options:0 metrics:@{@"height":@(self.imageViewPointer.image.size.height)} views:viewsDictionary]];
Ответ 2
Проблемы, которые возникают, это изменение местоположения изображения во время процесса масштабирования. Место происхождения изображения будет меняться как отрицательное значение во время масштабирования. Я считаю, что именно поэтому происходит резкое движение. Кроме того, после завершения масштабирования imageView все еще находится в неправильном месте, что означает, что свитки будут отображаться как смещенные.
Если вы реализуете -(void) scrollViewDidZoom:(UIScrollView *)scrollView
и записываете кадр UIImageView в течение этого времени, вы можете увидеть, как изменяется его начало.
Я закончил работу над тем, чтобы реализовать такую стратегию, как this
И, кроме того, изменение рамки contentView при масштабировании
-(void) scrollViewDidZoom:(UIScrollView *)scrollView {
CGRect cFrame = self.contentView.frame;
cFrame.origin = CGPointZero;
self.contentView.frame = cFrame;
}
Ответ 3
Эти решения все работают. Вот что я сделал, никаких хаков или подклассов не требуется, с этой настройкой:
[view]
[scrollView]
[container]
[imageView]
[imageView2]
- В IB подключите верх, верх, нижний и конечный от
scrollView
до view
.
- Подключите верх, ведущий, нижний и конечный от
container
до scrollView
.
- Подсоедините центр-x и center-y от
container
к центру-x и центру-y scrollView
и отметьте его как удалить во время сборки. Это необходимо только для того, чтобы отключить предупреждения в IB.
- Реализуйте
viewForZoomingInScrollView:
в контроллере представления, который должен быть делегатом scrollView, и из этого метода верните container
.
-
При настройке изображения imageView
определите минимальный масштаб масштабирования (так как сейчас он будет отображаться в собственном размере) и установите его:
CGSize mySize = self.view.bounds.size;
CGSize imgSize = _imageView.image.size;
CGFloat scale = fminf(mySize.width / imgSize.width,
mySize.height / imgSize.height);
_scrollView.minimumZoomScale = scale;
_scrollView.zoomScale = scale;
_scrollView.maximumZoomScale = 4 * scale;
Это работает для меня, при установке изображения масштабируется вид прокрутки, чтобы показать все изображение и позволяет увеличить до 4x начального размера.
Ответ 4
Скажем, у вас в раскадровке "UIImageView
" внутри "UIScrollView
" внутри "UIView
".
Свяжите все ограничения в "UIScrollView
" с контроллером представления + двумя ограничениями в UIView (Horizontal Space - Scroll View - View
и Horizontal Space - Scroll View - View
).
установите делегат AS контроллера вида для "UIScrollView
" .
Затем реализуем этот код:
@interface VC () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray* constraints;
@end
@implementation FamilyTreeImageVC
- (void)viewDidLoad {
[super viewDidLoad];
[self.scrollView removeConstraints:self.constraints];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
-(void) scrollViewDidZoom:(UIScrollView *)scrollView {
CGRect cFrame = self.imageView.frame;
cFrame.origin = CGPointZero;
self.imageView.frame = cFrame;
}
Ответ 5
У меня была такая же проблема при попытке реализовать масштабирование из раскадрованного проекта, используя только scrollView.
Я установил его, добавив отдельный распознаватель жестов. Я просто вытащил его из инструментария на свою сцену. Затем я подключил его к действию, которое я назвал "doPinch", который реализует масштабирование. Я подключил его к розетке, которую я назвал "pinchRecognizer", чтобы получить доступ к свойству scale. Это, похоже, отменяет встроенный зум scrollView, и дрожание исчезает. Возможно, это не делает ту же ошибку с происхождением, или обрабатывает это более изящно. Это очень мало работы над макетом в IB.
Как только вы вводите распознаватель жестов на сцену, вам понадобятся как действия, так и методы viewForZoomingInScrollView. Пропустите также, и масштабирование перестанет работать.
Код в моем контроллере просмотра:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.zoomableImage;
}
- (IBAction)doPinch:(id)sender
{
NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale);
[scrollView setZoomScale:self.pinchRecognizer.scale animated:NO];
}
Эта очень простая реализация имеет побочный эффект: когда вы возвращаетесь к увеличенному изображению и увеличиваете масштаб, значение масштаба равно 1.0f, поэтому оно возвращается к исходной шкале.
Вы можете отсортировать это, введя свойство "currentScale", чтобы отслеживать масштаб и устанавливать масштаб распознавателя жестов при повторном масштабировании. Вам нужно использовать свойство state распознавателя жестов:
- (IBAction)doPinch:(id)sender
{
NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale);
NSLog(@"Gesture recognizer state is: %d", self.pinchRecognizer.state);
switch (self.pinchRecognizer.state)
{
case 1:
NSLog(@"Zoom begins, with current scale set to: %f", self.currentScale);
[self.pinchRecognizer setScale:self.currentScale];
break;
case 3:
NSLog(@"Zoom ends, with pinch recognizer scale set to: %f", self.pinchRecognizer.scale);
self.currentScale = self.pinchRecognizer.scale;
default:
break;
}
[scrollView setZoomScale:self.pinchRecognizer.scale animated:NO];
}
Ответ 6
Так вот что мне удалось решить.
Здесь оригинал с моими изменениями:
@interface ScrollViewZoomTestViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIImageView* imageViewPointer;
// These properties are new
@property (nonatomic, strong) NSMutableArray* imageViewConstraints;
@property (nonatomic) BOOL imageViewConstraintsNeedUpdating;
@property (nonatomic, strong) UIScrollView* scrollViewPointer;
@end
@implementation ScrollViewZoomTestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scrollView = [[UIScrollView alloc] init];
UIImageView *imageView = [[UIImageView alloc] init];
[imageView setImage:[UIImage imageNamed:@"pic.jpg"]];
[self.view addSubview:scrollView];
[scrollView addSubview:imageView];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.imageViewPointer = imageView;
// New
self.scrollViewPointer = scrollView;
scrollView.maximumZoomScale = 2;
scrollView.minimumZoomScale = .5;
scrollView.delegate = self;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView);
NSLog(@"Current views dictionary: %@", viewsDictionary);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
// Saving the image view width & height constraints
self.imageViewConstraints = [[NSMutableArray alloc] init];
// Constrain the image view to be the same width & height of the scroll view
[_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]];
[_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]];
// Add the image view constraints to the VIEW, not the scroll view
[self.view addConstraints:_imageViewConstraints];
// Flag
self.imageViewConstraintsNeedUpdating = YES;
}
Итак, чтобы найти здесь, я добавляю все ограничения для self.view, сохраняя ограничения, установленные в UIImageView в свойстве NSMutableArray, и устанавливая флаг, который необходимо ограничить ограничениями UIImageView.
Эти начальные ограничения для UIImageView работают, чтобы настроить его для начала. Он будет такой же ширины и высоты, что и UIScrollView. Однако это НЕ позволит нам увеличить изображение. Он будет иметь ту же ширину/высоту, что и прокрутка. Не то, что мы хотим. Вот почему я сохраняю ограничения и устанавливаю флаг. Мы позаботимся об этом через минуту.
Теперь установите представление для масштабирования:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageViewPointer;
}
Итак, теперь нам нужно фактически увеличить масштаб. Я удаляю исходные ограничения UIImageView и добавляю некоторые новые, на этот раз ограничивая ширину и высоту contentSize UIScrollView:
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
if(_imageViewConstraintsNeedUpdating)
{
// Remove the previous image view constraints
[self.view removeConstraints:_imageViewConstraints];
// Replace them with new ones, this time constraining against the width & height of the scroll view content, not the scroll view itself
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollViewPointer, _imageViewPointer);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageViewPointer(width)]|" options:0 metrics:@{@"width" : @(_scrollViewPointer.contentSize.width)} views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageViewPointer(height)]|" options:0 metrics:@{@"height" : @(_scrollViewPointer.contentSize.height)} views:viewsDictionary]];
self.imageViewConstraintsNeedUpdating = NO;
}
}
@end
Мы не можем установить ограничения, подобные этому в -viewDidLoad, потому что изображение еще не было отображено в UIImageView, поэтому UIScrollView contentSize будет {0,0}.
Это кажется мне довольно взломанным, но он работает, он использует чистый автоматический макет, и я не могу найти лучший способ сделать это. Мне кажется, что Apple должна обеспечить лучший способ увеличения содержимого в UIScrollView и использовать ограничения Auto Layout.