Zrozumieć metody convertrect:toview:, convertrect:FromView:, convertpoint:toview: oraz convertpoint:fromview:

Staram się zrozumieć funkcjonalność tych metod. Czy mógłbyś podać mi prosty sposób na zrozumienie ich semantyki?

Z Dokumentacji, na przykład, metoda convertPoint:fromView: jest opisana następująco:

Zamienia punkt z układu współrzędnych danego widoku na punkt odbiornika.

Co oznacza układ współrzędnych ? A co z odbiornikiem ?

Na przykład, czy to sprawia, że sens użycia convertPoint:fromView: Jak Poniżej?

CGPoint p = [view1 convertPoint:view1.center fromView:view1];

Używając narzędzia NSLog, zweryfikowałem, że wartość p pokrywa się z centrum view1.

Z góry dziękuję.

EDIT: dla zainteresowanych stworzyłem prosty fragment kodu, aby zrozumieć te metody.

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)];
view1.backgroundColor = [UIColor redColor];

NSLog(@"view1 frame: %@", NSStringFromCGRect(view1.frame));        
NSLog(@"view1 center: %@", NSStringFromCGPoint(view1.center));   

CGPoint originInWindowCoordinates = [self.window convertPoint:view1.bounds.origin fromView:view1];        
NSLog(@"convertPoint:fromView: %@", NSStringFromCGPoint(originInWindowCoordinates));

CGPoint originInView1Coordinates = [self.window convertPoint:view1.frame.origin toView:view1];        
NSLog(@"convertPoint:toView: %@", NSStringFromCGPoint(originInView1Coordinates));

W obu przypadkach self.okno jest odbiornikiem. Ale jest różnica. W pierwszym przypadku parametr convertPoint jest wyrażony we współrzędnych view1. Wyjście jest "po": {]}

ConvertPoint: fromView: {100, 100}

W drugim, zamiast tego, convertPoint jest wyrażony w superview (self.okno). Wyjście jest następujące:

ConvertPoint: toView: {0, 0}

Author: Lorenzo B, 2011-12-11

9 answers

Każdy Widok ma swój własny układ współrzędnych - o początku 0,0 oraz szerokości i wysokości. Jest to opisane w bounds Prostokąt widoku. frame widoku, jednak będzie miał swój początek w punkcie w granicach prostokąta jego superview.

Zewnętrzny widok hierarchii widoków ma swoje źródło w 0,0, co odpowiada lewej górnej części ekranu w systemie iOS.

Jeśli dodasz do tego widoku subview o wartości 20,30, to punkt o wartości 0,0 w subview odpowiada miejsce z wynikiem 20,30 w supergigancie. Ta konwersja jest tym, co robią te metody.

Twój przykład powyżej jest bezcelowy (nie zamierzony kalambur), ponieważ konwertuje punkt z punktu widzenia na siebie, więc nic się nie stanie. Najczęściej można dowiedzieć się, gdzie znajduje się jakiś punkt widzenia w stosunku do jego superview - aby sprawdzić, czy Widok przesuwa się poza ekran, na przykład:

CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];

"Odbiornik" jest standardowym terminem objective-c dla obiektu, który odbiera wiadomość (metody są znany również jako wiadomości), więc w moim przykładzie tutaj odbiornikiem jest superview.

 170
Author: jrturton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-26 07:15:33

Oto Wyjaśnienie po angielsku.

Gdy chcesz przekonwertować rect subview (aView jest subviewem [aView superview]) na przestrzeń współrzędnych innego widoku (self).

// So here I want to take some subview and put it in my view's coordinate space
_originalFrame = [[aView superview] convertRect: aView.frame toView: self];
 28
Author: horseshoe7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-29 02:56:06

Zawsze uważam to za mylące, więc stworzyłem plac zabaw, na którym możesz wizualnie zbadać, co robi funkcja convert. Jest to zrobione w Swift 3 i Xcode 8.1 b:

Import UIKit import PlaygroundSupport

Class MyViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    // Main view
    view.backgroundColor = .black
    view.frame = CGRect(x: 0, y: 0, width: 500, height: 500)

    // Red view
    let redView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460))
    redView.backgroundColor = .red
    view.addSubview(redView)

    // Blue view
    let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420))
    blueView.backgroundColor = .blue
    redView.addSubview(blueView)

    // Orange view
    let orangeView = UIView(frame: CGRect(x: 20, y: 20, width: 380, height: 380))
    orangeView.backgroundColor = .orange
    blueView.addSubview(orangeView)

    // Yellow view
    let yellowView = UIView(frame: CGRect(x: 20, y: 20, width: 340, height: 100))
    yellowView.backgroundColor = .yellow
    orangeView.addSubview(yellowView)


    // Let's try to convert now
    var resultFrame = CGRect.zero
    let randomRect: CGRect = CGRect(x: 0, y: 0, width: 100, height: 50)

    /*
     func convert(CGRect, from: UIView?)
     Converts a rectangle from the coordinate system of another view to that of the receiver.
    */

    // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view:
    resultFrame = view.convert(randomRect, from: yellowView)

    // Try also one of the following to get a feeling of how it works:
    // resultFrame = view.convert(randomRect, from: orangeView)
    // resultFrame = view.convert(randomRect, from: redView)
    // resultFrame = view.convert(randomRect, from: nil)

    /*
     func convert(CGRect, to: UIView?)
     Converts a rectangle from the receiver’s coordinate system to that of another view.
     */

    // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view
    resultFrame = yellowView.convert(randomRect, to: view)
    // Same as what we did above, using "from:"
    // resultFrame = view.convert(randomRect, from: yellowView)

    // Also try:
    // resultFrame = orangeView.convert(randomRect, to: view)
    // resultFrame = redView.convert(randomRect, to: view)
    // resultFrame = orangeView.convert(randomRect, to: nil)


    // Add an overlay with the calculated frame to self.view
    let overlay = UIView(frame: resultFrame)
    overlay.backgroundColor = UIColor(white: 1.0, alpha: 0.9)
    overlay.layer.borderColor = UIColor.black.cgColor
    overlay.layer.borderWidth = 1.0
    view.addSubview(overlay)
}

}

Var Ctrl = MyViewController() PlaygroundPage.aktualne.liveView = ctrl.Widok

Pamiętaj, aby pokazać asystenta redaktora () aby zobaczyć widoki, należy wygląda tak:

Tutaj wpisz opis obrazka

Zapraszam do dodawania więcej przykładów tutaj lub w ten gist.

 26
Author: phi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-07 15:16:57

Każdy widok w iOS ma układ współrzędnych. Układ współrzędnych jest jak wykres, który ma oś x (linia pozioma) i oś y (linia pionowa). Punkt, w którym interesują się linie, nazywany jest początkiem. Punkt jest reprezentowany przez (x, y). Na przykład (2, 1) oznacza, że punkt ma 2 piksele w lewo, a 1 piksel w dół.

Możesz przeczytać więcej o układach współrzędnych tutaj - http://en.wikipedia.org/wiki/Coordinate_system

Ale musisz wiedzieć, że w iOS, każdy Widok ma swój własny układ współrzędnych, gdzie lewy górny róg to początek. Oś X rośnie w prawo, a oś y rośnie w dół.

Dla pytania o przeliczanie punktów, weźmy ten przykład.

Istnieje widok, zwany V1, który ma 100 pikseli szerokości i 100 pikseli wysokości. W środku znajduje się inny widok, zwany V2, w (10, 10, 50, 50), co oznacza, że (10, 10) jest punktem w układzie współrzędnych V1, w którym powinien znajdować się lewy górny róg V2, i (50, 50) to szerokość i wysokość V2. Weźmy punkt wewnątrz układu współrzędnych V2, powiedzmy (20, 20). Co to za punkt wewnątrz układu współrzędnych V1? Do tego służą metody (oczywiście można je samodzielnie obliczyć, ale oszczędzają dodatkową pracę). Dla przypomnienia, punkt w V1 będzie (30, 30).

Mam nadzieję, że to pomoże.

 13
Author: Saurav Sachidanand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-11 17:18:16

Dziękuję wszystkim za zamieszczenie pytania i odpowiedzi: pomogło mi to rozwiązać.

Mój kontroler widoku ma normalny widok.

Wewnątrz tego widoku znajduje się wiele widoków grupujących, które nie tylko zapewniają widoki potomne czystą interakcję z ograniczeniami układu automatycznego.

Wewnątrz jednego z tych widoków grupujących mam przycisk Dodaj, który prezentuje kontroler widoku wyskakującego, w którym użytkownik wprowadza pewne informacje.

view
--groupingView
----addButton

Podczas urządzenia obrót kontroler widoku jest alarmowany poprzez wywołanie uipopoverviewcontrollerdelegate popoverController: willRepositionPopoverToRect: inView:

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
    *rect = [self.addButton convertRect:self.addbutton.bounds toView:*view];
}

Zasadniczą częścią, która wynika z wyjaśnień podanych przez dwie pierwsze odpowiedzi powyżej, było to, że rect, z którego musiałem przekształcić bounds przycisku Dodaj, a nie jego ramki.

Nie próbowałem tego z bardziej złożoną hierarchią widoków, ale podejrzewam, że używając widoku dostarczonego w wywołaniu metody (inView:) omijamy komplikacje wielowarstwowego widoku liści.

 7
Author: tobinjim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 15:57:35

Użyłem tego postu, aby zastosować w moim przypadku. Mam nadzieję, że pomoże to innemu czytelnikowi w przyszłości.

Widok może widzieć tylko jego bezpośrednie widoki dzieci i rodziców. Nie widzi swoich dziadków ani wnuków.

Więc, w moim przypadku, mam widok nadrzędny o nazwie self.view, w tym self.view dodałem podviews o nazwie self.child1OfView, self.child2OfView. W self.child1OfView dodałem podwidywacze o nazwie self.child1OfView1, self.child2OfView1.
Teraz, jeśli fizycznie przenieść self.child1OfView1 do obszaru poza granicą self.child1OfView do miejsce na self.view, a następnie obliczyć nową pozycję dla self.child1OfView1 w obrębie self.view:

CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView];
 3
Author: user523234,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-13 02:04:51

Możesz zobaczyć poniższy kod, dzięki czemu możesz zrozumieć, jak to naprawdę działa.

    let scrollViewTemp = UIScrollView.init(frame: CGRect.init(x: 10, y: 10, width: deviceWidth - 20, height: deviceHeight - 20))

override func viewDidLoad() {
    super.viewDidLoad()

    scrollViewTemp.backgroundColor = UIColor.lightGray
    scrollViewTemp.contentSize = CGSize.init(width: 2000, height: 2000)
    self.view.addSubview(scrollViewTemp)

    let viewTemp = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 150, height: 150))
    viewTemp.backgroundColor = UIColor.green
    self.view.addSubview(viewTemp)

    let viewSecond = UIView.init(frame: CGRect.init(x: 100, y: 700, width: 300, height: 300))
    viewSecond.backgroundColor = UIColor.red
    self.view.addSubview(viewSecond)

    self.view.convert(viewTemp.frame, from: scrollViewTemp)
    print(viewTemp.frame)


    /*  First take one point CGPoint(x: 10, y: 10) of viewTemp frame,then give distance from viewSecond frame to this point.
     */
    let point = viewSecond.convert(CGPoint(x: 10, y: 10), from: viewTemp)
    //output:   (10.0, -190.0)
    print(point)

    /*  First take one point CGPoint(x: 10, y: 10) of viewSecond frame,then give distance from viewTemp frame to this point.
     */
    let point1 = viewSecond.convert(CGPoint(x: 10, y: 10), to: viewTemp)
    //output:  (10.0, 210.0)
    print(point1)

    /*  First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewSecond frame,then give distance from viewTemp frame to this rect.
     */
    let rect1 = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), to: viewTemp)
    //output:  (10.0, 210.0, 20.0, 20.0)
    print(rect1)

    /* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewTemp frame,then give distance from viewSecond frame to this rect.
     */
    let rect = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), from: viewTemp)
    //output:  (10.0, -190.0, 20.0, 20.0)
    print(rect)

}
 3
Author: vikas prajapati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-27 07:32:43

Przeczytałem odpowiedź i Rozumiem mechanikę, ale myślę, że ostateczny przykład nie jest poprawny. Zgodnie z API doc, właściwość center widoku zawiera znany środkowy punkt widoku w układzie współrzędnych superview.

Jeśli tak jest, to myślę, że nie ma sensu próbować poprosić superview o konwersję środka subview z układu współrzędnych subview, ponieważ wartość nie znajduje się w układzie współrzędnych subview. Co miałoby sens to zrobić odwrotnie, tj. przekształcić z układu współrzędnych superwizji w układ subwizji...

Można to zrobić na dwa sposoby (oba powinny dawać tę samą wartość):

CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview];

Lub

CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview];
Czy nie rozumiem, jak to powinno działać?
 1
Author: Moonwalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-07 13:03:26

Jeszcze jedna ważna kwestia dotycząca korzystania z tych API. Upewnij się, że nadrzędny łańcuch widoków jest kompletny między konwertowanym rect i widokiem Do/from. Na przykład-aView, bView i cView -

  • aView jest subview bView
  • chcemy przekonwertować obraz.frame to cView

Jeśli spróbujemy wykonać metodę przed dodaniem bview jako subview cView, otrzymamy odpowiedź bunk. Niestety nie ma wbudowanej ochrony w metody tego case. Może się to wydawać oczywiste, ale jest to coś, o czym należy pamiętać w przypadkach, gdy nawrócenie przechodzi przez długi łańcuch rodziców.

 1
Author: D-avid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-21 14:12:43