programing tip

iPhone 및 iPad에서 자동 레이아웃 왼쪽 여백이 다른 UITableViewCell

itbloger 2020. 12. 3. 07:37
반응형

iPhone 및 iPad에서 자동 레이아웃 왼쪽 여백이 다른 UITableViewCell


UITableView옵션 화면 / 장면에 정적 셀 그룹 사용하고 있습니다. 모든 작업은 자동 레이아웃을 사용하여 Xcode 6.1 / iOS 8.1.x / Storyboard에서 수행됩니다. 테이블 그룹에는 혼합 된 유형의 셀이 있으며 문제를 일으키는 두 가지 유형이 있습니다.

  1. 사용자 정의 스타일 및
  2. 스타일이 "오른쪽 세부 사항"인 셀

셀 # 1에서 레이블과 선행 컨테이너 사이의 왼쪽 여백에 대한 제약 조건을 설정할 수 있습니다. 셀 # 2에서 내가 아는 한 Interface Builder에서 제약 조건을 설정할 수 없습니다. 셀 # 1의 레이블에 왼쪽 여백을 설정하여 셀 # 2의 레이블과 맞 춥니 다. iPhone에서는 모든 것이 괜찮아 보이지만 테이블 뷰의 컨테이너 크기가 화면 크기의 절반 인 iPad에서 동일한 테이블을 표시하면 셀 # 2가 더 많은 여백 (동적으로?)을 얻는 반면 셀 # 1은 절대 여백을 유지합니다. 제약에 설정합니다. 또한 "relative to margin"속성을 사용하여 셀 # 1의 왼쪽 여백을 변경하려고했지만 아무 소용이 없었습니다.

iPhone :

iPhone의 표

iPad (Tableview 너비 = 1/2 화면 크기)

iPad의 표

따라서 질문은 다음과 같습니다. 셀 # 1의 레이블에 대한 제약 조건을 어떻게 설정하여 셀 # 2처럼 정렬되도록합니다.

여기에 문제를 보여주는 Xcode 6.1 샘플 프로젝트에 대한 링크도 있습니다. iPhone 및 iPad에서 실행하여 차이점을 확인하십시오.

https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.zip

이 질문은 iPhone 및 iPad 용 레이아웃 정적 테이블 셀 과 관련이있을 수 있지만 모든 것이 이제 적응해야하기 때문에 iOS 8에서도 다를 수 있습니다. 그래서 어쨌든이 질문을 게시하기로 결정했습니다.


 그것을 고치는 방법

많은 샘플 프로젝트와 스크린 샷으로 애플 버그보고 팀과 싸우고 그 답변을 분석 한 후 사용자 지정 스타일 셀이 여백과 관련하여 일관되게 작동하고 기본 UITableViewCells처럼 동작하도록하는 솔루션이 있다는 것을 발견했습니다. 다음 (주로 Becky의 답변을 기반으로하여 무엇이 다른지 그리고 무엇이 나를 위해 작동하게 만들 었는지 강조했습니다) :

  1. IB에서 셀의 콘텐츠보기 선택
  2. 크기 검사기로 이동
  3. 레이아웃 여백 섹션에서 Superview 여백 유지를 선택합니다 (더하기 기호를 클릭하지 마십시오).

    수퍼 뷰 여백 유지 확인

  4. (그리고 여기에 핵심이 있습니다) 셀 자체에 대해 동일 하게 수행하십시오 (원하는 경우 콘텐츠보기의 상위 항목).

    이번에는 콘텐츠보기가 아닌 셀

  5. 제약 조건을 다음과 같이 설정하십시오. Label.Leading = Superview.Leading Margin (상수 0)

    사용자 지정 셀의 레이블에 대한 제약 조건 설정

이제 모든 셀에 기본 셀과 일치하는 레이블이 있습니다! 이것은 Xcode 7 이상에서 나를 위해 작동하며 내가 언급 한 스레드에서 언급 한 수정 사항을 포함합니다. 이제 IB와 시뮬레이터에 올바르게 정렬 된 레이블이 표시됩니다.

시뮬레이터의 최종 결과

예를 들어 View Controller의 클래스에서 프로그래밍 방식으로이 중 일부를 수행 할 수도 있습니다.

cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true

또는 시작할 때 UIAppearance를 한 번 호출하여 설정할 수 있습니다 (Swift 만 알고 있습니다, 죄송합니다).

UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true

작동 원리와 이유

이든이 친절하게 지적하여 UIView에 애플의 문서는 설명 preservesSuperviewLayoutMargins으로 다음과 같습니다 :

When the value of this property is true, the superview’s margins are also considered when laying out content. This margin affects layouts where the distance between the edge of a view and its superview is smaller than the corresponding margin. For example, you might have a content view whose frame precisely matches the bounds of its superview. When any of the superview’s margins is inside the area represented by the content view and its own margins, UIKit adjusts the content view’s layout to respect the superview’s margins. The amount of the adjustment is the smallest amount needed to ensure that content is also inside the superview’s margins.

Therefore, if you want your cell's content to align with the TableView's margins (it's great-grandparent if you will), you need to have your content's two ascendants, Content View and the Table Cell itself, preserve the margins of their own superview.

Why this isn't default behavior surprises me : I feel like most developers who don't want to customize everything would expect this "inheritance" by default.


I ran in the same problem as you did and came up with a solution.

First, a little background: Since iOS 8, default table view cells respect the cell's layoutMargins to adapt for different traits (aka screens aka devices). For instance, layout margins on all iPhones (except iPhone 6 Plus when shown in a form sheet) are {8, 16, 8, 16}. On iPad they're {8, 20, 8, 20}. So now we know that there 4 pixels difference, which most likely your custom table view cell doesn't respect.

Your table view cell subclass needs to adapt the left margin constraint when layoutMargins change.

Here's the relevant code snippet:

 - (void)layoutMarginsDidChange
{
    [super layoutMarginsDidChange];

    self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
    self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}

Adapting to the layout margins in code enables you getting always the right padding for your title label.

You may also take a look at one of my UITableViewCell subclasses that already respect layoutMargins: https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m

Cheers


After reading through the existing answers and not finding an obvious programmatic solution, I did some more digging and now have a good answer for anyone else facing this issue.

First off, it's not necessary to set preservesSuperviewLayoutMargins to the cell's view or content view as other answers imply. While the default value is false, changing it to true had no noticeable effect that I could see.

The key for making this actually work is the layoutMarginsGuide property on UIView. Using this value, we can just easily pin the leadingAnchor of any subview to the leadingAnchor of the guide. Here's how it looks in code (and may very well be what IB is doing behind the scenes as in Jonas's answer).

In a UITableViewCell subclass, you would do something like this:

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide
    let leading = margins.leadingAnchor
    subview1.leadingAnchor.constraintEqualToAnchor(leading).active = true
    subview2.leadingAnchor.constraintEqualToAnchor(leading).active = true

    super.updateConstraints()
}

Swift 4.1 update

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide
    let leading = margins.leadingAnchor
    subview1.leadingAnchor.constraint(equalTo: leading).isActive = true
    subview2.leadingAnchor.constraint(equalTo: leading).isActive = true

    super.updateConstraints()
}

That's all! If you're developing for iOS versions pre-iOS 9, you'll need to substitute out the layout anchors and use the layoutMargins inset instead.


Note: I wrote a library to make the anchor pinning a little prettier, if you'd prefer a cleaner syntax. It's called SuperLayout and is available on Cocoapods. At the top of your source file, import SuperLayout:

import SuperLayout

And then in your layout block, use ~~, ≤≤, and ≥≥ to pin constraints:

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide

    subview1.leadingAnchor ~~ margins.leadingAnchor
    subview2.leadingAnchor ~~ margins.leadingAnchor

    super.updateConstraints()
}

ios 11+: let margins = contentView.directionalLayoutMargins ... in case you need to adapt to LTR and RTL out of the box. I assume most folks do need that.


I was able to get cells with custom styles aligned with the standard cells by doing the following:

  1. In the Document Outline, select the "Content View" for the cell with the custom style.
  2. Go to the Size Inspector.
  3. Under the "Layout Margins" dropdown, hit the little plus symbol next to "Preserve Superview Margins."
  4. Select the iPad size class, which is "Regular Width x Regular Height."
  5. Check the checkbox next to "Preserve Superview Margins."
  6. Resolve any Auto Layout warnings by updating the frames.

이것은 Xcode 7에서 나를 위해 일했습니다. Xcode 6에서도 작동하기를 바랍니다.


iPad Air, OS 10.1.1에서 테스트 할 때이 문제가 발생했습니다. 표 머리글은 원래 있어야하는 것보다 훨씬 더 들여 쓰기가되었으며 가로 방향에서는 더 나빴습니다. 그러나 OS 11까지의 아이폰에서는 괜찮 았습니다.

놀라운 해결책은 테이블이 생성 된 직후에 다음과 같은 코드 줄이었습니다 (죄송합니다. 저는 C #에서만 작업하지만 Obj-C 및 Swift 등가물을 쉽게 해결할 수 있습니다).

myTableView.SeparatorInset = myTableView.SeparatorInset;

그런 다음 모든 것이 들여 쓰기되었습니다!

참고 URL : https://stackoverflow.com/questions/27420888/uitableviewcell-with-autolayout-left-margin-different-on-iphone-and-ipad

반응형