Program Tip

코코아 : 프레임과 경계의 차이점은 무엇입니까?

programtip 2020. 10. 3. 11:36
반응형

코코아 : 프레임과 경계의 차이점은 무엇입니까?


UIView하위 클래스에는 모두 frame속성이 있습니다 bounds. 차이점이 뭐야?


경계 의 AN 의 UIView는 은 IS 직사각형 자체 좌표계 (0,0)에 대해, 위치 (x, y) 및 크기 (폭, 높이)로 표현.

프레임 의 AN 의 UIView는 은 IS 사각형 이 포함 된 수퍼 상대 위치 (x, y)의 크기 (폭, 높이)로 표현.

따라서 수퍼 뷰의 25,25 (x, y)에 위치한 크기가 100x100 (너비 x 높이) 인 뷰를 상상해보십시오. 다음 코드는이 뷰의 경계와 프레임을 인쇄합니다.

// This method is in the view controller of the superview
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"bounds.origin.x: %f", label.bounds.origin.x);
    NSLog(@"bounds.origin.y: %f", label.bounds.origin.y);
    NSLog(@"bounds.size.width: %f", label.bounds.size.width);
    NSLog(@"bounds.size.height: %f", label.bounds.size.height);

    NSLog(@"frame.origin.x: %f", label.frame.origin.x);
    NSLog(@"frame.origin.y: %f", label.frame.origin.y);
    NSLog(@"frame.size.width: %f", label.frame.size.width);
    NSLog(@"frame.size.height: %f", label.frame.size.height);
}

이 코드의 출력은 다음과 같습니다.

bounds.origin.x: 0
bounds.origin.y: 0
bounds.size.width: 100
bounds.size.height: 100

frame.origin.x: 25
frame.origin.y: 25
frame.size.width: 100
frame.size.height: 100

따라서 두 경우 모두 경계 또는 프레임을보고 있는지 여부에 관계없이 뷰의 너비와 높이가 동일하다는 것을 알 수 있습니다. 다른 점은 뷰의 x, y 위치입니다. 경계의 경우 x 및 y 좌표는 뷰 자체에 상대적이므로 0,0에 있습니다. 그러나 프레임 x 및 y 좌표는 상위 뷰 내의 뷰 위치에 상대적입니다 (이전에 우리가 25,25에 있다고 말 했음).

도있다 훌륭한 프리젠 테이션 UIViews을 다룹니다. 프레임과 경계의 차이를 설명 할뿐만 아니라 시각적 예제도 보여주는 슬라이드 1-20을 참조하십시오.


짧은 답변

프레임 = 상위 뷰의 좌표계를 사용하는 뷰의 위치 및 크기

  • 중요 : 뷰를 상위에 배치

bounds = 자체 좌표계를 사용하는 뷰의 위치 및 크기

  • 중요 :보기의 콘텐츠 또는 하위보기를 자체 내에 배치

자세한 답변

저를 기억하는 데 도움 프레임을 , 나는 생각 벽에 액자 . 액자는보기의 경계와 같습니다. 벽에 원하는 곳에 그림을 걸 수 있습니다. 같은 방식으로 상위 뷰 (슈퍼 뷰라고도 함) 내에 원하는 위치에 뷰를 배치 할 수 있습니다. 상위 뷰는 벽과 같습니다. iOS에서 좌표계의 원점은 왼쪽 상단입니다. 뷰 프레임의 xy 좌표를 (0, 0)으로 설정하여 슈퍼 뷰의 원점에 뷰를 놓을 수 있습니다. 마치 벽의 맨 왼쪽 모서리에 그림을 걸어 놓는 것과 같습니다. 오른쪽으로 이동하려면 x를 늘리고 아래로 이동하려면 y를 늘립니다.

To help me remember bounds, I think of a basketball court where sometimes the basketball gets knocked out of bounds. You are dribbling the ball all over the basketball court, but you don't really care where the court itself is. It could be in a gym, or outside at a high school, or in front of your house. It doesn't matter. You just want to play basketball. In the same way, the coordinate system for a view's bounds only cares about the view itself. It doesn't know anything about where the view is located in the parent view. The bounds' origin (point (0, 0) by default) is the top left corner of the view. Any subviews that this view has are laid out in relation to this point. It is like taking the basketball to the front left corner of the court.

이제 프레임과 경계를 비교하려고 할 때 혼란이 생깁니다. 하지만 실제로 처음 보이는 것만 큼 나쁘지는 않습니다. 이해를 돕기 위해 그림을 사용합시다.

프레임 대 경계

왼쪽의 첫 번째 그림에는 상위 뷰의 왼쪽 상단에있는 뷰가 있습니다. 노란색 사각형은 뷰의 프레임을 나타냅니다. 오른쪽에 다시보기가 표시되지만 이번에는 상위보기가 표시되지 않습니다. 경계가 상위 뷰에 대해 알지 못하기 때문입니다. 녹색 직사각형은 뷰의 경계를 나타냅니다. 두 이미지 빨간색 점 은 프레임 또는 경계 원점나타냅니다 .

Frame
    origin = (0, 0)
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

여기에 이미지 설명 입력

따라서 프레임과 경계는 그 그림에서 정확히 동일했습니다. 서로 다른 예를 살펴 보겠습니다.

Frame
    origin = (40, 60)  // That is, x=40 and y=60
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

여기에 이미지 설명 입력

따라서 프레임의 xy 좌표를 변경하면 상위 뷰에서 이동하는 것을 볼 수 있습니다. 그러나 뷰 자체의 내용은 여전히 ​​똑같이 보입니다. 경계는 무엇이 다른지 전혀 모릅니다.

지금까지 프레임과 경계의 너비와 높이는 정확히 동일했습니다. 하지만 항상 그런 것은 아닙니다. 뷰를 시계 방향으로 20도 회전하면 어떻게되는지보세요. (회전은 변환을 사용하여 수행됩니다. 자세한 내용은 설명서 와이 보기레이어 예제 를 참조하십시오.)

Frame
    origin = (20, 52)  // These are just rough estimates.
    width = 118
    height = 187

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

여기에 이미지 설명 입력

경계가 여전히 동일하다는 것을 알 수 있습니다. 그들은 여전히 ​​무슨 일이 일어 났는지 모릅니다! 하지만 프레임 값은 모두 변경되었습니다.

이제 프레임과 경계의 차이를 더 쉽게 알 수 있지 않습니까? You Probably Do n't Understand frames and bounds 기사 는 뷰 프레임을 다음과 같이 정의합니다.

... 해당 뷰에 적용된 변환을 포함하여 부모 좌표계와 관련하여 해당 뷰의 가장 작은 경계 상자입니다.

뷰를 변환하면 프레임이 정의되지 않습니다. 그래서 실제로 위 이미지에서 회전 된 녹색 경계 주위에 그린 노란색 프레임은 실제로 존재하지 않습니다. 즉, 회전, 크기 조정 또는 다른 변형을 수행하는 경우 더 이상 프레임 값을 사용하지 않아야합니다. 그래도 경계 값을 사용할 수 있습니다. Apple 문서는 다음과 같이 경고합니다.

중요 : 뷰의 transform속성에 ID 변환이 포함되지 않은 경우 해당 뷰의 프레임은 정의되지 않으며 자동 크기 조정 동작의 결과도 마찬가지입니다.

자동 크기 조정에 대해 오히려 불행합니다 .... 그래도 할 수있는 일이 있습니다.

Apple 문서 상태 :

transform보기 속성을 수정할 때 모든 변환은보기의 중심점을 기준으로 수행됩니다.

따라서 변환이 완료된 후 부모에서 뷰를 이동해야하는 경우 view.center좌표 를 변경하여 수행 할 수 있습니다 . 마찬가지로 frame, center부모 뷰의 좌표 시스템을 사용합니다.

자, 회전을 없애고 경계에 집중합시다. 지금까지 경계 원점은 항상 (0, 0)에 머물 렀습니다. 그래도 그럴 필요는 없습니다. 뷰에 너무 커서 한 번에 모두 표시 할 수없는 큰 하위 뷰가있는 경우 어떻게해야합니까? UIImageView큰 이미지로 만들어 보겠습니다 . 위의 두 번째 그림이 다시 있지만 이번에는 뷰의 하위 뷰의 전체 내용이 어떻게 보이는지 볼 수 있습니다.

Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

여기에 이미지 설명 입력

이미지의 왼쪽 상단 모서리 만 뷰의 경계 안에 들어갈 수 있습니다. 이제 경계의 원점 좌표를 변경하면 어떻게되는지보세요.

Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds 
    origin = (280, 70)
    width = 80
    height = 130

여기에 이미지 설명 입력

프레임은 수퍼 뷰에서 이동하지 않았지만 경계 사각형의 원점이 뷰의 다른 부분에서 시작하기 때문에 프레임 내부의 내용이 변경되었습니다. 이것은 a이면의 전체 아이디어 UIScrollView이며 하위 클래스 (예 : a UITableView)입니다. 자세한 설명은 UIScrollView 이해를 참조하십시오 .

프레임 사용시기 및 경계 사용시기

frame뷰의 위치를 ​​상위 뷰와 관련시키기 때문에 너비를 변경하거나 뷰와 상위 뷰 상단 사이의 거리를 찾는 외부 변경을 수행 할 때이를 사용합니다 .

보기 내에서 사물을 그리거나 하위보기를 배열하는 것과 같이 내부적으로 변경할bounds 때를 사용하십시오 . 또한 변형을 수행 한 경우 경계를 사용하여보기의 크기를 가져옵니다.

추가 연구를위한 기사 :

Apple 문서

관련 StackOverflow 질문

기타 자원

스스로 연습하세요

위의 기사를 읽는 것 외에도 테스트 앱을 만드는 데 많은 도움이됩니다. 비슷한 작업을 시도 할 수도 있습니다. ( 이 비디오 코스 에서 아이디어를 얻었 지만 불행히도 무료가 아닙니다.)

여기에 이미지 설명 입력

참조 용 코드는 다음과 같습니다.

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var myView: UIView!

    // Labels
    @IBOutlet weak var frameX: UILabel!
    @IBOutlet weak var frameY: UILabel!
    @IBOutlet weak var frameWidth: UILabel!
    @IBOutlet weak var frameHeight: UILabel!
    @IBOutlet weak var boundsX: UILabel!
    @IBOutlet weak var boundsY: UILabel!
    @IBOutlet weak var boundsWidth: UILabel!
    @IBOutlet weak var boundsHeight: UILabel!
    @IBOutlet weak var centerX: UILabel!
    @IBOutlet weak var centerY: UILabel!
    @IBOutlet weak var rotation: UILabel!

    // Sliders
    @IBOutlet weak var frameXSlider: UISlider!
    @IBOutlet weak var frameYSlider: UISlider!
    @IBOutlet weak var frameWidthSlider: UISlider!
    @IBOutlet weak var frameHeightSlider: UISlider!
    @IBOutlet weak var boundsXSlider: UISlider!
    @IBOutlet weak var boundsYSlider: UISlider!
    @IBOutlet weak var boundsWidthSlider: UISlider!
    @IBOutlet weak var boundsHeightSlider: UISlider!
    @IBOutlet weak var centerXSlider: UISlider!
    @IBOutlet weak var centerYSlider: UISlider!
    @IBOutlet weak var rotationSlider: UISlider!

    // Slider actions
    @IBAction func frameXSliderChanged(sender: AnyObject) {
        myView.frame.origin.x = CGFloat(frameXSlider.value)
        updateLabels()
    }
    @IBAction func frameYSliderChanged(sender: AnyObject) {
        myView.frame.origin.y = CGFloat(frameYSlider.value)
        updateLabels()
    }
    @IBAction func frameWidthSliderChanged(sender: AnyObject) {
        myView.frame.size.width = CGFloat(frameWidthSlider.value)
        updateLabels()
    }
    @IBAction func frameHeightSliderChanged(sender: AnyObject) {
        myView.frame.size.height = CGFloat(frameHeightSlider.value)
        updateLabels()
    }
    @IBAction func boundsXSliderChanged(sender: AnyObject) {
        myView.bounds.origin.x = CGFloat(boundsXSlider.value)
        updateLabels()
    }
    @IBAction func boundsYSliderChanged(sender: AnyObject) {
        myView.bounds.origin.y = CGFloat(boundsYSlider.value)
        updateLabels()
    }
    @IBAction func boundsWidthSliderChanged(sender: AnyObject) {
        myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
        updateLabels()
    }
    @IBAction func boundsHeightSliderChanged(sender: AnyObject) {
        myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
        updateLabels()
    }
    @IBAction func centerXSliderChanged(sender: AnyObject) {
        myView.center.x = CGFloat(centerXSlider.value)
        updateLabels()
    }
    @IBAction func centerYSliderChanged(sender: AnyObject) {
        myView.center.y = CGFloat(centerYSlider.value)
        updateLabels()
    }
    @IBAction func rotationSliderChanged(sender: AnyObject) {
        let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
        myView.transform = rotation
        updateLabels()
    }

    private func updateLabels() {

        frameX.text = "frame x = \(Int(myView.frame.origin.x))"
        frameY.text = "frame y = \(Int(myView.frame.origin.y))"
        frameWidth.text = "frame width = \(Int(myView.frame.width))"
        frameHeight.text = "frame height = \(Int(myView.frame.height))"
        boundsX.text = "bounds x = \(Int(myView.bounds.origin.x))"
        boundsY.text = "bounds y = \(Int(myView.bounds.origin.y))"
        boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
        boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
        centerX.text = "center x = \(Int(myView.center.x))"
        centerY.text = "center y = \(Int(myView.center.y))"
        rotation.text = "rotation = \((rotationSlider.value))"

    }

}

아래 코드를 실행 해보십시오

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}

이 코드의 출력은 다음과 같습니다.

케이스 장치 방향은 세로입니다.

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}

케이스 장치 방향은 가로입니다.

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}

분명히 프레임과 경계의 차이를 볼 수 있습니다.


프레임 과의 UIView를 정의하는 직사각형 의 수퍼 대하여 .

RECT 경계 되도록 정의 값의 범위 NSView의 좌표계입니다.

즉,이 직사각형의 모든 것이 실제로 UIView에 표시됩니다.


frame 은 super view의 좌표계에서보기의 원점 (왼쪽 상단 모서리) 및 크기입니다. 즉, 프레임 원점을 변경하여 super view에서보기를 변환하고, 반면에 경계 는 해당 크기와 원점입니다. 자체 좌표계이므로 기본적으로 경계 원점은 (0,0)입니다.

대부분의 경우 프레임과 경계는 합동이지만, 예를 들어 프레임 ((140,65), (200,250)) 및 경계 ((0,0), (200,250))의 뷰가 있고 뷰가 기울어 진 경우 따라서 오른쪽 하단 모서리에 서 있으면 경계는 여전히 ((0,0), (200,250))이되지만 프레임은.

여기에 이미지 설명 입력

프레임은 뷰를 캡슐화 / 둘러싸는 가장 작은 직사각형이므로 (사진에서와 같이) 프레임은 ((140,65), (320,320))이됩니다.

또 다른 차이점은 예를 들어 경계가 ((0,0), (200,200)) 인 superView가 있고이 superView에 프레임이 ((20,20), (100,100)) 인 subView가 있고 superView 경계를 변경 한 경우입니다. ((20,20), (200,200)), 그러면 subView 프레임은 여전히 ​​((20,20), (100,100))이지만 수퍼 뷰 좌표계가 (20, 20).

누군가에게 도움이되기를 바랍니다


SuperView를 기준으로 프레임을 구성하고 NSView를 기준으로 Bounds를 지정합니다.

예 : X = 40, Y = 60. 또한 3 개의 뷰를 포함합니다.이 다이어그램은 명확한 아이디어를 보여줍니다.

틀

범위


위의 답변은 Bounds와 Frames의 차이점을 잘 설명했습니다.

경계 : 자체 좌표계에 따른 뷰 크기 및 위치.

프레임 : SuperView에 상대적인보기 크기 및 위치입니다.

그러면 Bounds의 경우 X, Y가 항상 "0"이된다는 혼동이 있습니다. 이것은 사실이 아닙니다 . 이것은 UIScrollView 및 UICollectionView에서도 이해할 수 있습니다.

경계의 x, y는 0이 아닙니다
. UIScrollView가 있다고 가정 해 보겠습니다. 페이지 매김을 구현했습니다. UIScrollView에는 3 페이지가 있고 ContentSize의 너비는 화면 너비의 3 배입니다 (ScreenWidth가 320이라고 가정). 높이는 일정합니다 (200으로 가정).

scrollView.contentSize = CGSize(x:320*3, y : 200)

세 개의 UIImageView를 subView로 추가하고 프레임 x 값을 자세히 살펴보십시오.

let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 :  UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 :  UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
  1. 페이지 0 : ScrollView가 0 페이지에있을 때 경계는 (x : 0, y : 0, 너비 : 320, 높이 : 200)입니다.

  2. 1 페이지 : 스크롤하여 1 페이지로 이동합니다.
    이제 경계는 (x : 320, y : 0, 너비 : 320, 높이 : 200) 자체 좌표계와 관련하여 말한 것을 기억하십시오. 이제 ScrollView의 "Visible Part"는 320에서 "x"를 갖습니다. imageView1의 프레임을보십시오.

  3. 2 페이지 : 스크롤하여 2 페이지로 이동 경계 : (x : 640, y : 0, 너비 : 320, 높이 : 200) 다시 imageView2의 프레임을 살펴 봅니다.

UICollectionView의 경우와 동일합니다. collectionView를 보는 가장 쉬운 방법은 스크롤하고 경계를 인쇄 / 로그하는 것입니다. 그러면 아이디어를 얻을 수 있습니다.


위의 모든 답변은 정확하며 이것이 내 생각입니다.

프레임과 경계를 구별하려면 CONCEPTS 개발자는 다음을 읽어야합니다.

  1. 수퍼 뷰 (하나의 상위 뷰)와 관련하여 = FRAME 내에 포함됩니다.
  2. 자체 좌표계를 기준으로 하위 뷰 위치를 결정합니다. = BOUNDS

"bounds"는 좌표가 설정된 뷰의 위치라는 인상을주기 때문에 혼란 스럽습니다. 그러나 이들은 관계에 있으며 프레임 상수에 따라 조정됩니다.

iPhone 화면


프레임 = 상위 뷰의 좌표계를 사용하는 뷰의 위치 및 크기

bounds = 자체 좌표계를 사용하는 뷰의 위치 및 크기

뷰는 프레임 직사각형과 경계 직사각형이라는 두 개의 직사각형을 사용하여 크기와 위치를 추적합니다. 프레임 직사각형은 superview의 좌표계를 사용하여 superview에서 view의 위치와 크기를 정의합니다. 경계 사각형은 원점 및 배율을 포함하여 뷰의 내용을 그릴 때 사용되는 내부 좌표계를 정의합니다. 그림 2-1은 왼쪽의 프레임 사각형과 오른쪽의 경계 사각형 간의 관계를 보여줍니다.”

요컨대, 프레임은 뷰에 대한 수퍼 뷰의 아이디어이고 경계는 뷰 자체의 아이디어입니다. 각 뷰에 하나씩 여러 좌표계를 갖는 것은 뷰 계층의 일부입니다.


5 센트를 더하겠습니다.

프레임 은 뷰의 상위 뷰에서 상위 뷰 내부에 배치하는 데 사용됩니다.

경계 는보기 자체에서 자체 콘텐츠를 배치하는 데 사용됩니다 (스크롤보기가 스크롤하는 동안 수행하는 것처럼). clipsToBounds를 참조하십시오 . 경계는 뷰의 내용을 확대 / 축소하는데도 사용할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/1210047/cocoa-whats-the-difference-between-the-frame-and-the-bounds

반응형