2013-05-03 3 views
11

У меня есть UIScrollView. В этом я имею UIView, у которого есть кадр с отрицательным происхождением - мне нужно ограничить просмотр прокрутки, чтобы вы не могли прокручивать весь вид.UIScrollView масштабирование с представлением -ve

Я внедрил Zoom в этом прокрутке.

Когда Масштабирование вида прокрутки отрегулирует размер масштабируемого вида в соответствии с масштабом. НО ЭТО НЕ РЕГУЛИРУЕТ ПРОИСХОЖДЕНИЕ.

Так что, если у меня есть вид с рамой {0, -500}, {1000, 1000}

двутавровых масштаба к шкале от 0.5, это даст мне новый кадр {0, -500}, {500, 500}

Очевидно, что это нехорошо, весь вид увеличен из прокрутки. Я хочу, чтобы кадр {0, -250}, {500, 500}

Я могу исправить вещи немного в методе scrollViewDidZoom путем корректировки координат правильно .. Это делает работу, но масштаб не плавный. Изменение места происшествия приводит к его переходу.

я замечаю в документации UIView говорится (в отношении имущества кадра):

Предупреждения: Если свойства преобразования не является преобразованием личности, значение этого свойства не определенно и, следовательно, должно быть игнорируются.

Не совсем уверен, что это такое.

Я подхожу к этой проблеме неправильно? Каков наилучший способ исправить это?

Благодаря


Ниже приведены некоторые исходный код из тестового приложения я использую:

В ViewController ..

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.bigView = [[BigView alloc] initWithFrame: CGRectMake(0, -400, 1000, 1000)]; 

    [self.bigScroll addSubview: bigView]; 
    self.bigScroll.delegate = self; 
    self.bigScroll.minimumZoomScale = 0.2; 
    self.bigScroll.maximumZoomScale = 5; 
    self.bigScroll.contentSize = bigView.bounds.size; 
} 

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 
    return bigView; 
} 

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {  
// bigView.frame = CGRectMake(0, -400 * scrollView.zoomScale, 
//        bigView.frame.size.width, bigView.frame.size.height); 

    bigView.center = CGPointMake(500 * scrollView.zoomScale, 100 * scrollView.zoomScale); 
} 

А потом в представлении ...

- (void)drawRect:(CGRect)rect 
{ 
    // Drawing code 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor); 
    CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor); 
    CGContextFillRect(ctx, CGRectMake(100, 500, 10, 10)); 

    for (int i = 0; i < 1000; i += 100) { 
     CGContextStrokeRect(ctx, CGRectMake(0, i, 1000, 3));   
    } 
} 

N что здесь прыгучесть более очевидна при больших масштабах масштабирования. В моем реальном приложении, где гораздо больше рисования и обработки происходит, прыжок становится все более очевидным во все времена.

+0

Что касается преобразования и рамки, документы слишком расплывчаты. Кадр, по существу, выводится из границ, центра и прикладной трансформации (и настройки удобства). 'frame.size' будет' bounds.size' масштабируется по горизонтальному и вертикальному масштабированию, применяемому матрицей преобразования (поэтому для представления размера 150x100 с матрицей из 'CGAffineTransformMakeScale (3.0, 5.0)', 'frame.size' будет 450x500). Для повернутых представлений 'frame.size' будет размером с наименьший вертикальный прямоугольник, который может удерживать повернутые (и масштабированные) границы, так что углы границ обзора касаются сторон рамки. – fzwo

+0

Вы испытываете в симуляторе или на устройстве? – foundry

+0

Мне удалось тиражировать резкое масштабирование. Мои первоначальные предложения не исправили это. Я не видел его, так как он относился к размеру смещения, и я тестировал меньшее смещение 100 точек, которое было гладким. Я разработал новое решение, используя 'CAShapeLayer'. См. Мой обновленный ответ. – foundry

ответ

10

Вам не нужно использовать свойство рамки - и не должно, учитывая твердое предупреждение Apple. В таких случаях вы можете использовать bounds и center для достижения вашего результата.

В вашем случае вы можете игнорировать все свойства subview. Предполагая, что ваш подтаблицы является viewForZoomingInScrollView вы можете использовать Scrollview в contentOffset и zoomScale свойства

- (void) setMinOffsets:(UIScrollView*)scrollView 
    { 
     CGFloat minOffsetX = MIN_OFFSET_X*scrollView.zoomScale; 
     CGFloat minOffsetY = MIN_OFFSET_Y*scrollView.zoomScale; 

     if (scrollView.contentOffset.x < minOffsetX 
      || scrollView.contentOffset.y < minOffsetY) { 

      CGFloat offsetX = (scrollView.contentOffset.x > minOffsetX)? 
           scrollView.contentOffset.x : minOffsetX; 

      CGFloat offsetY = (scrollView.contentOffset.y > minOffsetY)? 
           scrollView.contentOffset.y : minOffsetY; 

      scrollView.contentOffset = CGPointMake(offsetX, offsetY); 
     } 
    } 

Называйте это как из scrollViewDidScroll и scrollViewDidZoom в вашем Scrollview делегата.Это должно работать плавно, но если у вас есть сомнения, вы также можете реализовать его, подклассифицируя scrollView и вызывая его с помощью layoutSubviews. В своем примере с PhotoScroller Apple сосредотачивает контент scrollView, переопределяя layoutSubviews - хотя безумно они игнорируют свои собственные предупреждения и настраивают свойство рамки subview для достижения этого.

обновление

Описанный выше метод устраняет «отскока», как Scrollview ударяется это ограничивает. Если вы хотите сохранить отскок, вы можете напрямую изменить свойство центра мнения, вместо:

- (void) setViewCenter:(UIScrollView*)scrollView 
    { 
     UIView* view = [scrollView subviews][0]; 
     CGFloat centerX = view.bounds.size.width/2-MIN_OFFSET_X; 
     CGFloat centerY = view.bounds.size.height/2-MIN_OFFSET_Y; 

     centerX *=scrollView.zoomScale; 
     centerY *=scrollView.zoomScale; 

     view.center = CGPointMake(centerX, centerY); 
    } 

обновления 2

С обновленным вопросом (с кодом), я могу видеть, что ни одно из этих решений исправить вашу проблему. То, что, кажется, происходит, состоит в том, что чем больше вы делаете свое смещение, тем резче становится движение зума. С смещением 100 точек действие все еще довольно плавное, но со смещением 500 точек оно неприемлемо грубо. Это отчасти связано с вашей обычной процедурой drawRect и частично связано с (слишком большим) пересчетом в scrollView для отображения правильного контента. Итак, у меня есть другое решение ...

В вашем представлении ControlController установите границы вашего интерфейса/рамки вашего customView в нормальный (0,0). Вместо этого мы будем компенсировать контент, используя слои. Вам нужно будет добавить структуру QuartzCore в ваш проект и #import его в свое пользовательское представление.

В пользовательском представлении инициализируйте два CAShapeLayers - один для поля, другой для строк. Если они имеют один и тот же уровень заполнения и инсульта, вам нужен только один CAShapeLayer (для этого примера я изменил цвета заливки и штриха). Каждый CAShapeLayer поставляется с собственным CGContext, который вы можете инициализировать один раз на каждый слой с цветами, ширинами линий и т. Д. Затем, чтобы сделать CAShapelayer, все, что вам нужно сделать, это установить его свойство path с CGPath.

#import "CustomView.h" 
#import <QuartzCore/QuartzCore.h> 

@interface CustomView() 
@property (nonatomic, strong) CAShapeLayer* shapeLayer1; 
@property (nonatomic, strong) CAShapeLayer* shapeLayer2; 
@end 

@implementation CustomView 

    #define MIN_OFFSET_X 100 
    #define MIN_OFFSET_Y 500 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     [self initialiseLayers]; 
    } 
    return self; 
} 


- (void) initialiseLayers 
{ 
    CGRect layerBounds = CGRectMake(MIN_OFFSET_X,MIN_OFFSET_Y 
          , self.bounds.size.width + MIN_OFFSET_X 
          , self.bounds.size.height+ MIN_OFFSET_Y); 

    self.shapeLayer1 = [[CAShapeLayer alloc] init]; 
    [self.shapeLayer1 setFillColor:[UIColor clearColor].CGColor]; 
    [self.shapeLayer1 setStrokeColor:[UIColor yellowColor].CGColor]; 
    [self.shapeLayer1 setLineWidth:1.0f]; 
    [self.shapeLayer1 setOpacity:1.0f]; 

    self.shapeLayer1.anchorPoint = CGPointMake(0, 0); 
    self.shapeLayer1.bounds = layerBounds; 
    [self.layer addSublayer:self.shapeLayer1]; 

Установка границ - это критический бит. В отличие от представлений, которые зажимают свои подзоны, CALayers выйдут за пределы своего суперслоя. Вы собираетесь начать рисовать MIN_OFFSET_Y пунктов над верхней частью вашего вида и MIN_OFFSET_X слева. Это позволяет вам выводить контент за пределы содержимого вашего содержимого scrollView без использования scrollView для выполнения какой-либо дополнительной работы.

В отличие от представлений, суперслое не автоматически обрезает содержимое подслоев, которые лежат вне его границ. Вместо этого суперслое позволяет отображать его подслои во всей полноте по умолчанию.
(Apple Docs, Building a Layer Hierarchy)

self.shapeLayer2 = [[CAShapeLayer alloc] init]; 

    [self.shapeLayer2 setFillColor:[UIColor blueColor].CGColor]; 
    [self.shapeLayer2 setStrokeColor:[UIColor clearColor].CGColor]; 
    [self.shapeLayer2 setLineWidth:0.0f]; 
    [self.shapeLayer2 setOpacity:1.0f]; 

    self.shapeLayer2.anchorPoint = CGPointMake(0, 0); 
    self.shapeLayer2.bounds = layerBounds; 
    [self.layer addSublayer:self.shapeLayer2]; 

    [self drawIntoLayer1]; 
    [self drawIntoLayer2]; 
} 

указан путь Безье для каждого слоя формы, а затем передать его в:

- (void) drawIntoLayer1 { 

    UIBezierPath* path = [[UIBezierPath alloc] init]; 
    [path moveToPoint:CGPointMake(0,0)]; 

    for (int i = 0; i < self.bounds.size.height+MIN_OFFSET_Y; i += 100) { 
     [path moveToPoint: 
       CGPointMake(0,i)]; 
     [path addLineToPoint: 
       CGPointMake(self.bounds.size.width+MIN_OFFSET_X, i)]; 
     [path addLineToPoint: 
       CGPointMake(self.bounds.size.width+MIN_OFFSET_X, i+3)]; 
     [path addLineToPoint: 
       CGPointMake(0, i+3)]; 
     [path closePath]; 
    } 

    [self.shapeLayer1 setPath:path.CGPath]; 
} 

- (void) drawIntoLayer2 { 
    UIBezierPath* path = [UIBezierPath bezierPathWithRect: 
      CGRectMake(100+MIN_OFFSET_X, MIN_OFFSET_Y, 10, 10)]; 
    [self.shapeLayer2 setPath:path.CGPath]; 
} 

Это устраняет необходимость drawRect - вам нужно только перерисовывать свои слои, если вы изменяете свойство пути. Даже если вы измените свойство пути так часто, как вы бы назвали drawRect, рисунок теперь должен быть значительно более эффективным. И так как path - это анимированное свойство, вы также получаете анимацию, загружаемую бесплатно, если вам это нужно.

В вашем случае нам нужно только установить путь один раз, поэтому все работы выполняется один раз, при инициализации.

Теперь вы можете удалить любой центрирующий код из ваших методов делегата scrollView, он больше не нужен.

+0

Разве это не предотвратит «отскок», который вы получите, прокрутив за пределы? –

+0

@MongusPong - да. Если вы хотите сохранить отскок, измените его свойство центра (см. Мое обновление). – foundry

+0

Просто попробуйте это. Установка центра имеет тот же эффект, что и установка рамки. Когда вы увеличиваете масштаб изображения, изображение перескакивает вверх и вниз, оно не является гладким. –

Смежные вопросы