Jak powiększyć / pomniejszyć obiekt UIImage, gdy użytkownik szczypie ekran?

84

Chciałbym powiększyć / pomniejszyć obiekt UIImage, gdy użytkownik wykonuje standardową akcję szczypania w mojej aplikacji. Obecnie używam UIImageView do wyświetlania mojego obrazu, jeśli ten szczegół w jakikolwiek sposób pomaga.

Próbuję wymyślić, jak to zrobić, ale na razie nie mam szczęścia.

Jakieś wskazówki?

jpm
źródło

Odpowiedzi:

85

Innym prostym sposobem na to jest umieszczenie UIImageViewpliku wewnątrz UIScrollView. Jak tutaj opisuję , musisz ustawić contentSize widoku przewijania tak, aby był taki sam jak twój UIImageView'srozmiar. Ustaw instancję kontrolera jako delegata widoku przewijania i zaimplementuj metody viewForZoomingInScrollView:i scrollViewDidEndZooming:withView:atScale:, aby umożliwić powiększanie przez szczypanie i przesuwanie obrazu. To właśnie robi rozwiązanie Bena, tylko w nieco lżejszy sposób, ponieważ nie masz narzutu pełnego widoku internetowego.

Jednym z problemów, które możesz napotkać, jest to, że skalowanie w widoku przewijania ma postać przekształceń zastosowanych do obrazu. Może to prowadzić do rozmycia przy dużych współczynnikach powiększenia. Aby uzyskać coś, co można przerysować, możesz postępować zgodnie z moimi sugestiami , aby zapewnić wyraźniejszy obraz po zakończeniu gestu szczypania. Rozwiązanie hniels może zostać użyte w tym momencie do przeskalowania obrazu.

Brad Larson
źródło
2
Upewnij się, że zmieniłeś maximumZoomScale i / lub minimumZoomScale :).
Chris Prince,
94

Jak opisali inni, najłatwiejszym rozwiązaniem jest umieszczenie UIImageView w UIScrollView. Zrobiłem to w pliku .xib Interface Builder.

W viewDidLoad ustaw następujące zmienne. Ustaw swój kontroler jako UIScrollViewDelegate.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView.minimumZoomScale = 0.5;
    self.scrollView.maximumZoomScale = 6.0;
    self.scrollView.contentSize = self.imageView.frame.size;
    self.scrollView.delegate = self;
}

Aby zwrócić obraz, który chcesz powiększyć, musisz zaimplementować następującą metodę.

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

W wersjach starszych niż iOS9 może być również konieczne dodanie tej pustej metody delegata:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
}

Dokumentacja Jabłko robi dobrą robotę opisując jak to zrobić:

łobuz
źródło
3
Można usunąć scrollViewDidEndZooming: withView: teraz, testowałem z iOS9 Xcode7
ElonChan
1
Mi to pasuje! Bez scrollViewDidEndZooming :metody implementacji .
Johnny Zhang
48

Rozwiązanie Shefali dla UIImageView działa świetnie, ale wymaga niewielkiej modyfikacji:

- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded
        || gesture.state == UIGestureRecognizerStateChanged) {
        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.frame.size.width / self.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
        self.transform = transform;
        gesture.scale = 1;
    }
}

(Wadą rozwiązania Shefali było to, że nie skalowało się w sposób ciągły podczas szczypania. Ponadto podczas rozpoczynania nowego szczypania bieżąca skala obrazu została zresetowana.)

JRV
źródło
Chcę włączyć gest panoramowania po rozpoczęciu gestu uszczypnięcia. Jak to zrobić?
Gajendra K Chauhan
@Gajendra: W takim przypadku myślę, że powinieneś użyć osadzonych komponentów w widoku przewijania, który obsługuje oba po wyjęciu z pudełka
JRV
Ale chcę mieć funkcjonalność zarówno dla gestu stuknięcia, jak i gestu szczypania. Użyłem też siatki (warstw) w Imageview. Czy mogę wiedzieć, czy w widoku przewijania można używać gestów szczypania i stukania?
Gajendra K Chauhan
@GajendraKChauhan: Widok przewijania obsługuje szczypanie, aby powiększyć i przesuwać poza ramką - pamiętaj tylko o ustawieniu minimalnej / maksymalnej skali powiększenia, ale spójrz także na odpowiedź Brada Larsonsa. A jeśli chcesz również dotknąć gestu, możesz dodać go jako gest.
JRV
@JRV, jakich delegatów muszę dodać, aby plik self.frame.size.width został rozpoznany?
23

Poniższy kod pomaga powiększyć UIImageView bez użycia UIScrollView:

-(void)HandlePinch:(UIPinchGestureRecognizer*)recognizer{
    if ([recognizer state] == UIGestureRecognizerStateEnded) {
        NSLog(@"======== Scale Applied ===========");
        if ([recognizer scale]<1.0f) {
            [recognizer setScale:1.0f];
        }
        CGAffineTransform transform = CGAffineTransformMakeScale([recognizer scale],  [recognizer scale]);
        imgView.transform = transform;
    }
}
Shefali Soni
źródło
To jest najlepsze rozwiązanie :). Dzięki.
sabiland
19

Pamiętaj, że NIGDY nie powiększasz pliku UIImage. ZAWSZE.

Zamiast tego powiększasz i pomniejszasz widok, viewktóry wyświetla plik UIImage.

W tym konkretnym przypadku możesz zdecydować się na utworzenie UIViewniestandardowego rysunku z niestandardowym rysunkiem do wyświetlania obrazu, UIImageViewktóry wyświetla obraz za Ciebie lub UIWebViewbędzie wymagał dodatkowego kodu HTML, aby go wykonać.

We wszystkich przypadkach trzeba wdrożyć touchesBegan, touchesMovedi tym podobne, aby określić co użytkownik próbuje zrobić (zoom, pan, itp).

sierpień
źródło
+1 dla wyjaśnienia, nigdy nie myślałem o tym w ten sposób, wiwatuje stary
Elmo,
@ Sierpień, czy możesz podać mi przykład powiększania i obracania obrazu po dotknięciu Rozpoczęto, dotknięto przeniesiono?
Mitesh Dobareeya
7

Oto rozwiązanie, którego użyłem wcześniej, które nie wymaga korzystania z UIWebView.

- (UIImage *)scaleAndRotateImage(UIImage *)image
{
    int kMaxResolution = 320; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);


    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}

Artykuł można znaleźć na stronie wsparcia Apple pod adresem : http://discussions.apple.com/message.jspa?messageID=7276709#7276709

Niels Hansen
źródło
-1 dla strony pomocy hostowanej przez jsp. potrząsa pięścią przy składni kropki
esharp
Czy możesz mi podać, jak wywołać tę metodę (za pomocą myszy lub dotyku)?
Bhumesh Purohit
5

Odpowiedzi Shafali i JRV rozszerzyły się o przesuwanie i szczypanie, aby powiększyć:

#define MINIMUM_SCALE 0.5
#define MAXIMUM_SCALE 6.0
@property CGPoint translation;


- (void)pan:(UIPanGestureRecognizer *)gesture {
    static CGPoint currentTranslation;
    static CGFloat currentScale = 0;
    if (gesture.state == UIGestureRecognizerStateBegan) {
        currentTranslation = _translation;
        currentScale = self.view.frame.size.width / self.view.bounds.size.width;
    }
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {

        CGPoint translation = [gesture translationInView:self.view];

        _translation.x = translation.x + currentTranslation.x;
        _translation.y = translation.y + currentTranslation.y;
        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x , _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(currentScale, currentScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
    }
}


- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {
//        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.view.frame.size.width / self.view.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x, _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(newScale, newScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
        gesture.scale = 1;
    }
}
Paul King
źródło
nie wspominaj o odpowiedziach innych osób, ponieważ mogą one w każdej chwili usunąć swoją odpowiedź.
Enamul Hassan
3

Najprostszym sposobem, aby to zrobić, jeśli chcesz tylko powiększać szczypanie, jest umieszczenie obrazu wewnątrz UIWebView(napisz niewielką ilość kodu otoki html, odwołaj się do obrazu i w zasadzie gotowe). Im więcej complcated sposobem, aby to zrobić jest użycie touchesBegan, touchesMovedoraz touchesEndedśledzić palców użytkownika i dostosowanie widoku na przekształcać własność odpowiednio.

Ben Gottlieb
źródło
czy możesz zasugerować mi jakiś przykład związany z Twoją odpowiedzią. Jak mogę powiększać i obracać obraz za pomocą dotknięćPoczątek, dotknięciaPrzeniesione metody. Moje pytanie link => stackoverflow.com/questions/36833841/…
Mitesh Dobareeya
0

Pamiętaj, że nie chcesz powiększać / pomniejszać UIImage. Zamiast tego spróbuj powiększyć / pomniejszyć widok, który zawiera kontroler widoku UIImage.

Zrobiłem rozwiązanie tego problemu. Spójrz na mój kod:

@IBAction func scaleImage(sender: UIPinchGestureRecognizer) {
        self.view.transform = CGAffineTransformScale(self.view.transform, sender.scale, sender.scale)
        sender.scale = 1
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        view.backgroundColor = UIColor.blackColor()
    }

Uwaga: nie zapomnij podłączyć PinchGestureRecognizer.

G Azam
źródło