Mam UIImageView
wewnątrzUIScrollView
którego używam do powiększania i przewijania. Jeśli obraz / zawartość widoku przewijania jest większa niż widok przewijania, wszystko działa poprawnie. Jednak gdy obraz staje się mniejszy niż widok przewijania, przykleja się do lewego górnego rogu widoku przewijania. Chciałbym, aby był wyśrodkowany, tak jak aplikacja Zdjęcia.
Jakieś pomysły lub przykłady dotyczące utrzymania treści w UIScrollView
środku, gdy jest mniejsza?
Pracuję z iPhone'em 3.0.
Poniższy kod prawie działa. Obraz wraca do lewego górnego rogu, jeśli uszczypnę go po osiągnięciu minimalnego poziomu powiększenia.
- (void)loadView {
[super loadView];
// set up main scroll view
imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
[imageScrollView setBackgroundColor:[UIColor blackColor]];
[imageScrollView setDelegate:self];
[imageScrollView setBouncesZoom:YES];
[[self view] addSubview:imageScrollView];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
[imageView setTag:ZOOM_VIEW_TAG];
[imageScrollView setContentSize:[imageView frame].size];
[imageScrollView addSubview:imageView];
CGSize imageSize = imageView.image.size;
[imageView release];
CGSize maxSize = imageScrollView.frame.size;
CGFloat widthRatio = maxSize.width / imageSize.width;
CGFloat heightRatio = maxSize.height / imageSize.height;
CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
[imageScrollView setMinimumZoomScale:initialZoom];
[imageScrollView setZoomScale:1];
float topInset = (maxSize.height - imageSize.height) / 2.0;
float sideInset = (maxSize.width - imageSize.width) / 2.0;
if (topInset < 0.0) topInset = 0.0;
if (sideInset < 0.0) sideInset = 0.0;
[imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}
/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
[scrollView setZoomScale:scale+0.01 animated:NO];
[scrollView setZoomScale:scale animated:NO];
// END Bug workaround
CGSize maxSize = imageScrollView.frame.size;
CGSize viewSize = view.frame.size;
float topInset = (maxSize.height - viewSize.height) / 2.0;
float sideInset = (maxSize.width - viewSize.width) / 2.0;
if (topInset < 0.0) topInset = 0.0;
if (sideInset < 0.0) sideInset = 0.0;
[imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
Odpowiedzi:
Mam bardzo proste rozwiązanie! Wszystko, czego potrzebujesz, to zaktualizować środek widoku podrzędnego (widok obrazu) podczas powiększania ScrollViewDelegate. Jeśli powiększony obraz jest mniejszy niż widok przewijania, dostosuj subview.center w przeciwnym razie środek jest (0,0).
źródło
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentInset.left - scrollView.contentInset.right - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom - scrollView.contentSize.height) * 0.5, 0.0);
zoomToRect:
. Zastosowanie tegocontentInset
podejścia działa lepiej, jeśli potrzebujesz tej funkcji. Więcej informacji znajdziesz na petersteinberger.com/blog/2013/how-to-center-uiscrollview .Odpowiedź @ EvelynCordner była najlepsza w mojej aplikacji. Dużo mniej kodu niż inne opcje.
Oto wersja Swift, jeśli ktoś jej potrzebuje:
źródło
Okej, walczyłem z tym przez ostatnie dwa dni z przerwami i w końcu doszedłem do całkiem niezawodnego (jak dotąd ...) rozwiązania, pomyślałem, że powinienem się nim podzielić i zaoszczędzić innym trochę bólu. :) Jeśli znajdziesz problem z tym rozwiązaniem, krzycz!
Zasadniczo przejrzałem to, co wszyscy inni: przeszukując StackOverflow, fora programistów Apple, spojrzałem na kod dla three20, ScrollingMadness, ScrollTestSuite itp. Próbowałem powiększyć ramkę UIImageView, bawiąc się przesunięciem i / lub wstawkami UIScrollView z ViewController itp., ale nic nie działało świetnie (jak wszyscy inni też się przekonali).
Po spaniu na nim wypróbowałem kilka alternatywnych ujęć:
Dzięki tej podklasowej metodzie UIScrollView nadpisuję mutator contentOffset, więc nie ustawia {0,0}, gdy obraz jest skalowany mniejszy niż rzutnia - zamiast tego ustawia przesunięcie tak, aby obraz był wyśrodkowany w rzutni. Jak dotąd wydaje się, że zawsze działa. Sprawdziłem to z szerokimi, wysokimi, małymi i dużymi obrazami i nie ma problemu „działa, ale szczypanie przy minimalnym powiększeniu psuje”.
Wgrałem przykładowy projekt na github, który używa tego rozwiązania, możesz go znaleźć tutaj: http://github.com/nyoron/NYOBetterZoom
źródło
anOffset.y = -(scrollViewSize.height - zoomViewSize.height) / 2.0
naanOffset.y = (-(scrollViewSize.height - zoomViewSize.height) / 2.0) + 10;
Ten kod powinien działać na większości wersji iOS (i został przetestowany pod kątem działania od wersji 3.1).
Jest oparty na kodzie Apple WWDC dla fotokolera.
Dodaj poniższy kod do swojej podklasy UIScrollView i zastąp tileContainerView widokiem zawierającym obraz lub kafelki:
źródło
UIScrollView
podejście podklasy wydaje się lepsze: jest wywoływane, gdy widok jest wyświetlany po raz pierwszy + w sposób ciągły podczas powiększania (podczas gdyscrollViewDidZoom
jest wywoływany tylko raz na przewijanie i po fakcie)Aby uzyskać rozwiązanie lepiej dostosowane do widoków przewijania, które używają automatycznego układu, użyj wstawek zawartości widoku przewijania, zamiast aktualizować ramki widoków podrzędnych widoku przewijania.
źródło
UIView
czyli wewnątrzUIScrollview
do tego samego środka (view.center = scrollview.center;
), a następnie wscrollViewDidZoom
secieview.frame.origin
x
iy
aby0
ponownie.dispatch_async
do głównej kolejki wviewWillAppear
której wywołujęscrollViewDidZoom:
mój główny widok przewijania. To sprawia, że widok pojawia się z wyśrodkowanymi obrazami.viewDidLayoutSubviews
aby upewnić się, że jest on poprawnie skonfigurowany, gdy widok jest wyświetlany po raz pierwszy.Obecnie tworzę podklasy
UIScrollView
i nadpisuję,setContentOffset:
aby dostosować przesunięcie na podstawiecontentSize
. Działa zarówno z zoomem szczypiącym, jak i programowym.Oprócz tego, że jest krótki i słodki, ten kod zapewnia znacznie płynniejszy zoom niż rozwiązanie @Erdemus. Możesz zobaczyć to w akcji w demo RMGallery .
źródło
Spędziłem dzień walcząc z tym problemem i ostatecznie zaimplementowałem scrollViewDidEndZooming: withView: atScale: w następujący sposób:
Gwarantuje to, że gdy obraz jest mniejszy niż ekran, wokół niego jest wystarczająco dużo miejsca, dzięki czemu można go ustawić dokładnie w wybranym miejscu.
(zakładając, że Twój UIScrollView zawiera UIImageView do przechowywania obrazu)
Zasadniczo chodzi o to, aby sprawdzić, czy szerokość / wysokość widoku obrazu jest mniejsza niż szerokość / wysokość ekranu, a jeśli tak, utwórz wstawkę o połowę szerokości / wysokości ekranu (prawdopodobnie możesz to powiększyć, jeśli chcesz, aby obraz był wyjść poza granice ekranu).
Zauważ, że ponieważ jest to UIScrollViewDelegate metoda , nie zapomnij dodać jej do deklaracji kontrolera widoku, aby uniknąć problemu z kompilacją.
źródło
Firma Apple udostępniła nagrania z sesji WWDC 2010 wszystkim członkom programu dla programistów iPhone'a. Jednym z omawianych tematów jest sposób, w jaki stworzyli aplikację do zdjęć !!! Tworzą bardzo podobną aplikację krok po kroku i udostępniają cały kod za darmo.
Nie używa też prywatnego interfejsu API. Nie mogę umieścić tutaj żadnego kodu z powodu umowy o zachowaniu poufności, ale tutaj jest link do przykładowego kodu do pobrania. Prawdopodobnie będziesz musiał się zalogować, aby uzyskać dostęp.
http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645
A oto łącze do strony iTunes WWDC:
http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj
źródło
Ok, to rozwiązanie działa dla mnie. Mam podklasę
UIScrollView
z odniesieniem do tego,UIImageView
który jest wyświetlany. Przy każdymUIScrollView
powiększeniu właściwość contentSize jest dostosowywana. To w seterze odpowiednio wyskalujęUIImageView
i dostosowuję jego położenie środkowe.Zawsze ustawiam obraz na zmieniam jego
UIScrollView
rozmiar. WUIScrollView
widoku przewijania jest również utworzona przeze mnie niestandardowa klasa.Dzięki tym dwóm metodom mogę uzyskać widok, który zachowuje się tak jak aplikacja do zdjęć.
źródło
Sposób, w jaki to zrobiłem, polega na dodaniu dodatkowego widoku do hierarchii:
Podaj
UIView
ten sam współczynnik proporcji co twójUIScrollView
i wyśrodkujUIImageView
na tym.źródło
Wiem, że niektóre odpowiedzi powyżej są poprawne, ale chcę tylko udzielić odpowiedzi z pewnym wyjaśnieniem, komentarze pomogą Ci zrozumieć, dlaczego to lubimy.
Kiedy ładuję scrollView po raz pierwszy, piszę następujący kod, aby go wyśrodkować, proszę zauważyć, że ustawiliśmy
contentOffset
najpierw, a następniecontentInset
Następnie, gdy szczypię vContent, piszę następujący kod, aby go wyśrodkować.
źródło
Jeśli
contentInset
nie jest potrzebny do niczego innego, można go użyć do wyśrodkowania zawartości widoku przewijania.Zaletą tego podejścia jest to, że nadal można używać
contentLayoutGuide
do umieszczania treści w widoku scrollviewlub po prostu przeciągnij i upuść zawartość w Interface Builder Xcode.
źródło
Możesz obserwować
contentSize
właściwość UIScrollView (używając obserwacji klucz-wartość lub podobnego) i automatycznie dostosowywać,contentInset
gdycontentSize
zmiany są mniejsze niż rozmiar widoku przewijania.źródło
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
(opisanego na developer.apple.com/iphone/library/documentation/UIKit/… ), ale wolałbym obserwować,contentSize
ponieważ w przeciwnym razie i tak skończysz odrzucając nową skalę powiększenia i znajdując ją w widoku. (Chyba że potrafisz wyciągnąć niesamowitą matematykę na podstawie względnych rozmiarów twoich widoków.)Jednym z eleganckich sposobów na wyśrodkowanie treści
UISCrollView
jest to.Dodaj jednego obserwatora do contentSize swojego
UIScrollView
, więc ta metoda będzie wywoływana za każdym razem, gdy zmieni się zawartość ...Teraz twoja metoda obserwatora:
Powinieneś zmienić
[pointer viewWithTag:100]
. Zastąp widokiem zawartościUIView
.imageGallery
wskazanie na rozmiar okna.To poprawi środek treści za każdym razem, gdy zmieni się jego rozmiar.
UWAGA: Jedynym sposobem, w jaki ta zawartość nie działa zbyt dobrze, jest standardowa funkcja powiększania w
UIScrollView
.źródło
To jest moje rozwiązanie tego problemu, które działa całkiem nieźle w przypadku każdego widoku w przewijanym widoku.
źródło
Jest tu mnóstwo rozwiązań, ale zaryzykowałbym umieszczenie tutaj własnego. Jest to dobre z dwóch powodów: nie psuje funkcji powiększania, podobnie jak aktualizacja ramki widoku obrazu w toku, a także szanuje oryginalne wstawki widoku przewijania (powiedzmy, zdefiniowane w xib lub storyboardie w celu wdzięcznej obsługi półprzezroczystych pasków narzędzi itp.) .
Najpierw zdefiniuj małego pomocnika:
Aby zachować oryginalne wstawki, zdefiniowane pole:
I gdzieś w widoku DidLoad wypełnij go:
Aby umieścić UIImageView w UIScrollView (zakładając, że sam UIImage jest w loadImage var):
scrollViewDidZoom: from UIScrollViewDelegate dla tego widoku przewijania:
Wreszcie, skupiając się:
Nie testowałem jeszcze zmiany orientacji (tj. Poprawnej reakcji na zmianę rozmiaru samego UIScrollView), ale naprawienie tego powinno być stosunkowo łatwe.
źródło
Przekonasz się, że rozwiązanie opublikowane przez Erdemusa działa, ale… Istnieją przypadki, w których metoda scrollViewDidZoom nie jest wywoływana i obraz jest zablokowany w lewym górnym rogu. Prostym rozwiązaniem jest jawne wywołanie metody podczas początkowego wyświetlania obrazu, na przykład:
W wielu przypadkach możesz wywoływać tę metodę dwukrotnie, ale jest to czystsze rozwiązanie niż niektóre inne odpowiedzi w tym temacie.
źródło
Przykład Apple Photo Scroller robi dokładnie to, czego szukasz. Umieść to w swojej podklasie UIScrollView i zmień _zoomView na UIImageView.
Przykładowy kod Apple Photo Scroller
źródło
Aby animacja przebiegała przyjemnie, ustaw
i skorzystaj z tej funkcji (znajdując środek używając metody podanej w tej odpowiedzi )
Tworzy to efekt odbijania się, ale wcześniej nie wymaga żadnych gwałtownych ruchów.
źródło
Tylko zatwierdzona odpowiedź szybko, ale bez tworzenia podklas przy użyciu delegata
źródło
W przypadku, gdy twój wewnętrzny imageView ma początkową określoną szerokość (np. 300) i chcesz po prostu wyśrodkować jego szerokość tylko przy powiększeniu mniejszym niż jego początkowa szerokość, może to również pomóc.
źródło
Oto obecny sposób, w jaki wykonuję tę pracę. Jest lepiej, ale nadal nie jest doskonały. Spróbuj ustawić:
aby rozwiązać problem polegający na tym, że widok nie jest wyśrodkowany, gdy w
minZoomScale
.Wykonuję również następujące czynności, aby zapobiec przyklejaniu się obrazu z boku po pomniejszeniu.
źródło
OK, myślę, że znalazłem całkiem dobre rozwiązanie tego problemu. Sztuczka polega na tym, aby stale regulować
imageView's
ramę. Uważam, że działa to znacznie lepiej niż ciągłe dostosowywaniecontentInsets
lubcontentOffSets
. Musiałem dodać trochę dodatkowego kodu, aby pomieścić zarówno obrazy pionowe, jak i poziome.Oto kod:
źródło
Po prostu wyłącz paginację, aby działało dobrze:
źródło
Miałem dokładnie ten sam problem. Oto jak rozwiązałem
Ten kod powinien zostać wywołany jako wynik
scrollView:DidScroll:
źródło
Chociaż pytanie jest trochę stare, problem nadal istnieje. Rozwiązałem to w Xcode 7 , wprowadzając ograniczenie przestrzeni pionowej najwyższego elementu (w tym przypadku
topLabel
) do superViews (thescrollView
) top an,IBOutlet
a następnie przeliczając jego stałą za każdym razem, gdy zawartość zmienia się w zależności od wysokości podwidoków scrollView (topLabel
ibottomLabel
).źródło