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.