Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/Vendor/MWPhotoBrowser/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 16 kB image not shown  

Quelle  MWZoomingScrollView.m   Sprache: Unknown

 

Spracherkennung für: .m vermutete Sprache: Abap {ABAP[40] Lisp[36] Masm[36]} [Methode: maximale Elemente, drei Dimensionen]

// This file is based on third party code, see below for the original author
// and original license.
// Modifications are (c) by Threema AG and licensed under the AGPLv3.

//
//  ZoomingScrollView.m
//  MWPhotoBrowser
//
//  Created by Michael Waterfall on 14/10/2010.
//  Copyright 2010 d3i. All rights reserved.
//

#import <DACircularProgress/DACircularProgressView.h>
#import "MWCommon.h"
#import "MWZoomingScrollView.h"
#import "MWPhotoBrowser.h"
#import "MWPhoto.h"
#import "MWPhotoBrowserPrivate.h"
#import "UIImage+MWPhotoBrowser.h"
#import <ThreemaFramework/ThreemaFramework-Swift.h>

// Private methods and properties
@interface MWZoomingScrollView () {
    
    MWPhotoBrowser __weak *_photoBrowser;
 MWTapDetectingView *_tapView; // for background taps
 MWTapDetectingImageView *_photoImageView;
 DACircularProgressView *_loadingIndicator;
    UIImageView *_loadingError;
    
}

@end

@implementation MWZoomingScrollView

- (id)initWithPhotoBrowser:(MWPhotoBrowser *)browser {
    if ((self = [super init])) {
        
        // Setup
        _index = NSUIntegerMax;
        _photoBrowser = browser;
        
  // Tap view for background
        if (!UIAccessibilityIsVoiceOverRunning()) {
            _tapView = [[MWTapDetectingView alloc] initWithFrame:self.bounds];
            _tapView.tapDelegate = self;
            _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
            /***** BEGIN THREEMA MODIFICATION *********/
            _tapView.backgroundColor = [Colors backgroundView];
            /***** BEGIN THREEMA MODIFICATION *********/
            [self addSubview:_tapView];
        }
  
  // Image view
  _photoImageView = [[MWTapDetectingImageView alloc] initWithFrame:CGRectZero];
  _photoImageView.tapDelegate = self;
        _photoImageView.contentMode = UIViewContentModeCenter;
        _photoImageView.contentMode = UIViewContentModeScaleAspectFit;
  _photoImageView.backgroundColor = [UIColor blackColor];
        _photoImageView.isAccessibilityElement = YES;
        _photoImageView.accessibilityTraits = UIAccessibilityTraitImage;
        _photoImageView.userInteractionEnabled = YES;
        _photoImageView.accessibilityIgnoresInvertColors = true;
  [self addSubview:_photoImageView];
  
  // Loading indicator
  _loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(140.0f, 30.0f, 40.0f, 40.0f)];
        _loadingIndicator.userInteractionEnabled = NO;
        _loadingIndicator.thicknessRatio = 0.1;
        _loadingIndicator.roundedCorners = NO;
  _loadingIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
        UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
  [self addSubview:_loadingIndicator];

        // Listen progress notifications
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(setProgressFromNotification:)
                                                     name:MWPHOTO_PROGRESS_NOTIFICATION
                                                   object:nil];
        
  // Setup
  self.backgroundColor = [UIColor blackColor];
  self.delegate = self;
  self.showsHorizontalScrollIndicator = NO;
  self.showsVerticalScrollIndicator = NO;
  self.decelerationRate = UIScrollViewDecelerationRateFast;
  self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        self.isAccessibilityElement = NO;
        
    }
    return self;
}

- (void)dealloc {
    if ([_photo respondsToSelector:@selector(cancelAnyLoading)]) {
        [_photo cancelAnyLoading];
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)prepareForReuse {
    [self hideImageFailure];
    self.photo = nil;
    self.captionView = nil;
    self.selectedButton = nil;
    self.playButton = nil;
    _photoImageView.hidden = NO;
    _photoImageView.image = nil;
    _index = NSUIntegerMax;
}

- (BOOL)displayingVideo {
    return [_photo respondsToSelector:@selector(isVideo)] && _photo.isVideo;
}

- (void)setImageHidden:(BOOL)hidden {
    _photoImageView.hidden = hidden;
}

#pragma mark - Image

- (void)setPhoto:(id<MWPhoto>)photo {
    // Cancel any loading on old photo
    if (_photo && photo == nil) {
        if ([_photo respondsToSelector:@selector(cancelAnyLoading)]) {
            [_photo cancelAnyLoading];
        }
    }
    _photo = photo;
    /***** BEGIN THREEMA MODIFICATION: add function *********/
    _photoImageView.accessibilityLabel = [_photo accessibilityLabelForContent];
    /***** END THREEMA MODIFICATION: add function *********/
    UIImage *img = [_photoBrowser imageForPhoto:_photo];
    if (img) {
        [self displayImage];
    } else {
        // Will be loading so show loading
        [self showLoadingIndicator];
    }
}

// Get and display image
- (void)displayImage {
 if (_photo && _photoImageView.image == nil) {
  
  // Reset
  self.maximumZoomScale = 1;
  self.minimumZoomScale = 1;
  self.zoomScale = 1;
  self.contentSize = CGSizeMake(0, 0);
  
  // Get image from browser as it handles ordering of fetching
  UIImage *img = [_photoBrowser imageForPhoto:_photo];
  if (img) {
   
   // Hide indicator
   [self hideLoadingIndicator];
   
   // Set image
   _photoImageView.image = img;
   _photoImageView.hidden = NO;
   
   // Setup photo frame
   CGRect photoImageViewFrame;
   photoImageViewFrame.origin = CGPointZero;
   photoImageViewFrame.size = img.size;
   _photoImageView.frame = photoImageViewFrame;
   self.contentSize = photoImageViewFrame.size;

   // Set zoom to minimum zoom
            [self setMaxMinZoomScalesForCurrentBounds];
   
  } else  {

            // Show image failure
            [self displayImageFailure];
   
  }
  [self setNeedsLayout];
 }
}

// Image failed so just show black!
- (void)displayImageFailure {
    [self hideLoadingIndicator];
    _photoImageView.image = nil;
    
    // Show if image is not empty
    if (![_photo respondsToSelector:@selector(emptyImage)] || !_photo.emptyImage) {
        if (!_loadingError) {
            _loadingError = [UIImageView new];
            _loadingError.image = [UIImage imageForResourcePath:@"MWPhotoBrowser.bundle/ImageError" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
            _loadingError.userInteractionEnabled = NO;
            _loadingError.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
            UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
            [_loadingError sizeToFit];
            [self addSubview:_loadingError];
        }
        _loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
                                         floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
                                         _loadingError.frame.size.width,
                                         _loadingError.frame.size.height);
    }
}

- (void)hideImageFailure {
    if (_loadingError) {
        [_loadingError removeFromSuperview];
        _loadingError = nil;
    }
}

#pragma mark - Loading Progress

- (void)setProgressFromNotification:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *dict = [notification object];
        id <MWPhoto> photoWithProgress = [dict objectForKey:@"photo"];
        if (photoWithProgress == self.photo) {
            float progress = [[dict valueForKey:@"progress"] floatValue];
            _loadingIndicator.progress = MAX(MIN(1, progress), 0);
        }
    });
}

- (void)hideLoadingIndicator {
    _loadingIndicator.hidden = YES;
}

- (void)showLoadingIndicator {
    self.zoomScale = 0;
    self.minimumZoomScale = 0;
    self.maximumZoomScale = 0;
    _loadingIndicator.progress = 0;
    _loadingIndicator.hidden = NO;
    [self hideImageFailure];
}

#pragma mark - Setup

- (CGFloat)initialZoomScaleWithMinScale {
    CGFloat zoomScale = self.minimumZoomScale;
    if (_photoImageView && _photoBrowser.zoomPhotosToFill) {
        // Zoom image to fill if the aspect ratios are fairly similar
        CGSize boundsSize = self.bounds.size;
        CGSize imageSize = _photoImageView.image.size;
//        CGFloat boundsAR = boundsSize.width / boundsSize.height;
//        CGFloat imageAR = imageSize.width / imageSize.height;
        CGFloat xScale = boundsSize.width / imageSize.width;    // the scale needed to perfectly fit the image width-wise
//        CGFloat yScale = boundsSize.height / imageSize.height;  // the scale needed to perfectly fit the image height-wise
        
//        // Zooms standard portrait images on a 3.5in screen but not on a 4in screen.
//        if (ABS(boundsAR - imageAR) < 0.17) {
//            zoomScale = MAX(xScale, yScale);
//            // Ensure we don't zoom in or out too far, just in case
//            zoomScale = MIN(MAX(self.minimumZoomScale, zoomScale), self.maximumZoomScale);
//        }
        
        /***** BEGIN THREEMA MODIFICATION: zoom fix for iphone x *********/
        if (imageSize.width * zoomScale - boundsSize.width > 1) {
            zoomScale = xScale;
        }
        /***** END THREEMA MODIFICATION: zoom fix for iphone x *********/
    }
    return zoomScale;
}

- (void)setMaxMinZoomScalesForCurrentBounds {
    
    // Reset
    self.maximumZoomScale = 1;
    self.minimumZoomScale = 1;
    self.zoomScale = 1;
    
    // Bail if no image
    if (_photoImageView.image == nil) return;
    
    // Reset position
    _photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
 
    // Sizes
    CGSize boundsSize = self.bounds.size;
    CGSize imageSize = _photoImageView.image.size;
    
    // Calculate Min
    CGFloat xScale = boundsSize.width / imageSize.width;    // the scale needed to perfectly fit the image width-wise
    CGFloat yScale = boundsSize.height / imageSize.height;  // the scale needed to perfectly fit the image height-wise
    CGFloat minScale = MIN(xScale, yScale);                 // use minimum of these to allow the image to become fully visible
    
    // Calculate Max
    CGFloat maxScale = 3;
    if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
        // Let them go a bit bigger on a bigger screen!
        maxScale = 4;
    }
    
    // Image is smaller than screen so no zooming!
    if (xScale >= 1 && yScale >= 1) {
        minScale = 1.0;
    }
    
    // Set min/max zoom
    self.maximumZoomScale = maxScale;
    self.minimumZoomScale = minScale;
    
    // Initial zoom
    self.zoomScale = [self initialZoomScaleWithMinScale];
    
    // If we're zooming to fill then centralise
    if (self.zoomScale != minScale) {
        
        // Centralise
        self.contentOffset = CGPointMake((imageSize.width * self.zoomScale - boundsSize.width) / 2.0,
                                         (imageSize.height * self.zoomScale - boundsSize.height) / 2.0);

    }
    
    // Disable scrolling initially until the first pinch to fix issues with swiping on an initally zoomed in photo
    self.scrollEnabled = NO;
    
    // If it's a video then disable zooming
    if ([self displayingVideo]) {
        self.maximumZoomScale = self.zoomScale;
        self.minimumZoomScale = self.zoomScale;
    }

    // Layout
 [self setNeedsLayout];

}

#pragma mark - Layout

- (void)layoutSubviews {
 
 // Update tap view frame
 _tapView.frame = self.bounds;
 
 // Position indicators (centre does not seem to work!)
 if (!_loadingIndicator.hidden)
        _loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
                                         floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
                                         _loadingIndicator.frame.size.width,
                                         _loadingIndicator.frame.size.height);
 if (_loadingError)
        _loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
                                         floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
                                         _loadingError.frame.size.width,
                                         _loadingError.frame.size.height);

 // Super
 [super layoutSubviews];
 
    // Center the image as it becomes smaller than the size of the screen
    CGSize boundsSize = self.bounds.size;
    CGRect frameToCenter = _photoImageView.frame;
    
    // Horizontally
    if (frameToCenter.size.width < boundsSize.width) {
        frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
 } else {
        frameToCenter.origin.x = 0;
 }
    
    // Vertically
    if (frameToCenter.size.height < boundsSize.height) {
        frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
 } else {
        frameToCenter.origin.y = 0;
 }
    
 // Center
 if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
  _photoImageView.frame = frameToCenter;
 
}

#pragma mark - UIScrollViewDelegate

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

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
 [_photoBrowser cancelControlHiding];
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
    self.scrollEnabled = YES; // reset
 [_photoBrowser cancelControlHiding];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
 [_photoBrowser hideControlsAfterDelay];
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    [self setNeedsLayout];
    [self layoutIfNeeded];
}

#pragma mark - Tap Detection

- (void)handleSingleTap:(CGPoint)touchPoint {
    /***** BEGIN THREEMA MODIFICATION: video *********/
    if ([_photo respondsToSelector:@selector(handleSingleTap:)]) {
        [_photo handleSingleTap:touchPoint];
    } else {
        [_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
    }
    /***** END THREEMA MODIFICATION: video *********/
}

- (void)handleDoubleTap:(CGPoint)touchPoint {
    
    // Dont double tap to zoom if showing a video
    if ([self displayingVideo]) {
        return;
    }
 
 // Cancel any single tap handling
 [NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
    
    /***** BEGIN THREEMA MODIFICATION: video *********/
    if ([_photo canScaleImage] == NO) {
        return;
    }
    /***** END THREEMA MODIFICATION: video *********/

 
 // Zoom
 if (self.zoomScale != self.minimumZoomScale && self.zoomScale != [self initialZoomScaleWithMinScale]) {
  
  // Zoom out
  [self setZoomScale:self.minimumZoomScale animated:YES];
  
 } else {
  
  // Zoom in to twice the size
        CGFloat newZoomScale = ((self.maximumZoomScale + self.minimumZoomScale) / 2);
        CGFloat xsize = self.bounds.size.width / newZoomScale;
        CGFloat ysize = self.bounds.size.height / newZoomScale;
        [self zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];

 }
 
 // Delay controls
 [_photoBrowser hideControlsAfterDelay];
 
}

// Image View
- (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch { 
    [self handleSingleTap:[touch locationInView:imageView]];
}
- (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
    [self handleDoubleTap:[touch locationInView:imageView]];
}

// Background View
- (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
    // Translate touch location to image view location
    CGFloat touchX = [touch locationInView:view].x;
    CGFloat touchY = [touch locationInView:view].y;
    touchX *= 1/self.zoomScale;
    touchY *= 1/self.zoomScale;
    touchX += self.contentOffset.x;
    touchY += self.contentOffset.y;
    [self handleSingleTap:CGPointMake(touchX, touchY)];
}
- (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
    /***** BEGIN THREEMA MODIFICATION: video *********/
    if ([_photo canScaleImage] == NO) {
        return;
    }
    /***** END THREEMA MODIFICATION: video *********/

    // Translate touch location to image view location
    CGFloat touchX = [touch locationInView:view].x;
    CGFloat touchY = [touch locationInView:view].y;
    touchX *= 1/self.zoomScale;
    touchY *= 1/self.zoomScale;
    touchX += self.contentOffset.x;
    touchY += self.contentOffset.y;
    [self handleDoubleTap:CGPointMake(touchX, touchY)];
}


@end

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.