UIView에 영향을 미치는 모든 제약 조건 제거
몇 가지 제약 조건을 통해 화면에 배치되는 UIView가 있습니다. 제약 중 일부는 수퍼 뷰가 소유하고 다른 제약은 다른 조상이 소유합니다 (예 : UIViewController의 뷰 속성).
이 오래된 제약을 모두 제거하고 새로운 제약을 사용하여 새로운 곳에 배치하고 싶습니다.
모든 단일 제약 조건에 대해 IBOutlet을 생성하지 않고 어떤 뷰가 해당 제약 조건을 소유하는지 기억하지 않고 어떻게이 작업을 수행 할 수 있습니까?
정교하게 설명하기 위해 순진한 접근 방식은 각 제약 조건에 대해 IBOutlet을 생성 한 다음 다음과 같은 코드 호출을 포함하는 것입니다.
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
이 코드의 문제점은 가장 단순한 경우에도 2 개의 IBOutlet을 생성해야한다는 것입니다. 복잡한 레이아웃의 경우 필요한 IBOutlet 4 개 또는 8 개에 쉽게 도달 할 수 있습니다. 또한 제약 조건을 제거하기위한 호출이 적절한 뷰에서 호출되는지 확인해야합니다. 예를 들어, 그는 상상 myViewsLeftConstraint
에 의해 소유된다 viewA
. 실수로를 호출 [self.view removeConstraint:self.myViewsLeftConstraint]
하면 아무 일도 일어나지 않습니다.
참고 : constraintsAffectingLayoutForAxis 메소드 는 유망 해 보이지만 디버깅 목적으로 만 사용됩니다.
업데이트 : 내가 받고있는 대부분의 답변 self.constraints
은 self.superview.constraints
, 또는 일부 변형을 처리합니다. 이러한 방법은 뷰에 영향을주는 제약이 아닌 뷰가 소유 한 제약 만 반환하므로 이러한 솔루션은 작동 하지 않습니다 .
이러한 솔루션의 문제를 명확히하려면 다음 뷰 계층 구조를 고려하십시오.
- 할아버지
- 아버지
- 나를
- 아들
- 딸
- 동료
- 나를
- 삼촌
- 아버지
이제 다음 제약 조건을 만들고 항상 가장 가까운 공통 조상에 연결한다고 상상해보십시오.
- C0 : 나 : 아들과 같은 탑 (나 소유)
- C1 : 나 : 너비 = 100 (내가 소유)
- C2 : 나 : 형제와 같은 키 (아버지 소유)
- C3 : 나 : 삼촌과 같은상의 (할아버지 소유)
- C4 : 나 : 할아버지와 같은 왼쪽 (Grandfather 소유)
- C5 : 형제 : 아버지와 같은 왼쪽 (아버지 소유)
- C6 : 삼촌 : 할아버지와 같은 왼쪽 (할아버지 소유)
- C7 : 아들 : 딸과 같은 왼쪽 (나 소유)
이제에 영향을 미치는 모든 제약을 제거하고 싶다고 상상해보십시오 Me
. 적절한 솔루션은 제거해야 [C0,C1,C2,C3,C4]
하며 다른 것은 없어야 합니다.
내가 self.constraints
(self가 Me 인 경우)를 사용 하면를 얻을 것입니다 [C0,C1,C7]
. 왜냐하면 그것들은 Me가 소유 한 유일한 제약이기 때문입니다. 이것이 누락 되었기 때문에 분명히 이것을 제거하는 것으로 충분하지 않을 것 [C2,C3,C4]
입니다. 또한 C7
불필요하게 제거 하고 있습니다.
self.superview.constraints
(self가 Me 인 경우)를 사용하면 [C2,C5]
아버지가 소유 한 제약이기 때문에 를 얻습니다 . 이후 분명히 우리는 이것들을 제거 할 수 C5
는 전혀 관련이있다 Me
.
내가 사용 grandfather.constraints
하면 얻을 것이다 [C3,C4,C6]
. 다시 말하지만,이 모든 것을 제거 할 수는 없습니다 C6
.
브 루트 포스 접근법 (자체 포함) 뷰의 각 선조의 위에 루프이고, 만약 볼 firstItem
또는 secondItem
뷰 자체이다; 그렇다면 해당 제약 조건을 제거하십시오. 이것은 올바른 솔루션으로 이어지고 [C0,C1,C2,C3,C4]
, 그리고 그 제약들만 반환 합니다.
그러나 전체 조상 목록을 반복하는 것보다 더 우아한 해결책이 있기를 바랍니다.
이 접근 방식은 저에게 효과적이었습니다.
@interface UIView (RemoveConstraints)
- (void)removeAllConstraints;
@end
@implementation UIView (RemoveConstraints)
- (void)removeAllConstraints
{
UIView *superview = self.superview;
while (superview != nil) {
for (NSLayoutConstraint *c in superview.constraints) {
if (c.firstItem == self || c.secondItem == self) {
[superview removeConstraint:c];
}
}
superview = superview.superview;
}
[self removeConstraints:self.constraints];
self.translatesAutoresizingMaskIntoConstraints = YES;
}
@end
실행이 완료되면 자동 크기 조정 제약 조건을 생성하기 때문에 뷰가 그대로 유지됩니다. 이 작업을 수행하지 않으면 일반적으로보기가 사라집니다. 또한 상위 뷰에서 제약 조건을 제거하는 것이 아니라 상위 뷰에 영향을주는 제약 조건이있을 수 있으므로 계속 위로 이동합니다.
Swift 4 버전
extension UIView {
public func removeAllConstraints() {
var _superview = self.superview
while let superview = _superview {
for constraint in superview.constraints {
if let first = constraint.firstItem as? UIView, first == self {
superview.removeConstraint(constraint)
}
if let second = constraint.secondItem as? UIView, second == self {
superview.removeConstraint(constraint)
}
}
_superview = superview.superview
}
self.removeConstraints(self.constraints)
self.translatesAutoresizingMaskIntoConstraints = true
}
}
지금까지 찾은 유일한 해결책은 수퍼 뷰에서 뷰를 제거하는 것입니다.
[view removeFromSuperview]
이것은 레이아웃에 영향을 미치는 모든 제약을 제거하고 수퍼 뷰에 추가 할 준비가되어 있고 새로운 제약이 첨부 된 것처럼 보입니다. 그러나 계층 구조에서 모든 하위 뷰도 잘못 제거되고 잘못 제거됩니다 [C7]
.
다음을 수행하여 뷰의 모든 제약 조건을 제거 할 수 있습니다.
[_cell.contentView removeConstraints:_cell.contentView.constraints];
편집 : 모든 하위보기의 제약 조건을 제거하려면 Swift에서 다음 확장을 사용하십시오.
extension UIView {
func clearConstraints() {
for subview in self.subviews {
subview.clearConstraints()
}
self.removeConstraints(self.constraints)
}
}
Swift에서 :
import UIKit
extension UIView {
/**
Removes all constrains for this view
*/
func removeConstraints() {
let constraints = self.superview?.constraints.filter{
$0.firstItem as? UIView == self || $0.secondItem as? UIView == self
} ?? []
self.superview?.removeConstraints(constraints)
self.removeConstraints(self.constraints)
}
}
Apple Developer Documentation 에 따라이를 달성하는 방법에는 두 가지 방법이 있습니다.
1. NSLayoutConstraint.deactivateConstraints
이것은 한 번의 호출로 일련의 제약 조건을 비활성화하는 쉬운 방법을 제공하는 편리한 방법입니다. 이 메서드의 효과는 각 제약 조건의 isActive 속성을 false로 설정하는 것과 같습니다. 일반적으로이 방법을 사용하는 것이 각 제약 조건을 개별적으로 비활성화하는 것보다 효율적입니다.
// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])
// Usage
NSLayoutConstraint.deactivate(yourView.constraints)
2. UIView.removeConstraints
(> = iOS 8.0에서 더 이상 사용되지 않음)
iOS 8.0 이상용으로 개발하는 경우 removeConstraints : 메서드를 직접 호출하는 대신 NSLayoutConstraint 클래스의 deactivateConstraints : 메서드를 사용합니다. deactivateConstraints : 메서드는 올바른 뷰에서 제약 조건을 자동으로 제거합니다.
// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`
// Usage
yourView.removeConstraints(yourView.constraints)
팁
Storyboard
s 또는 XIB
s를 사용 하는 것은 시나리오에서 언급 한대로 제약 조건을 구성하는 데 매우 고통 스러울 수 있으므로 제거하려는 각 항목에 대해 IBOutlet을 만들어야합니다. 그럼에도 불구하고 대부분의 Interface Builder
경우 해결하는 것보다 더 많은 문제가 발생합니다.
따라서 매우 동적 인 콘텐츠와 뷰의 다른 상태를 가질 때 다음을 제안합니다.
- 프로그래밍 방식으로보기 만들기
- 레이아웃 및 NSLayoutAnchor 사용
- 나중에 제거 될 수있는 각 제약 조건을 배열에 추가합니다.
- 새 상태를 적용하기 전에 매번 지우십시오.
간단한 코드
private var customConstraints = [NSLayoutConstraint]()
private func activate(constraints: [NSLayoutConstraint]) {
customConstraints.append(contentsOf: constraints)
customConstraints.forEach { $0.isActive = true }
}
private func clearConstraints() {
customConstraints.forEach { $0.isActive = false }
customConstraints.removeAll()
}
private func updateViewState() {
clearConstraints()
let constraints = [
view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
view.topAnchor.constraint(equalTo: parentView.topAnchor),
view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
]
activate(constraints: constraints)
view.layoutIfNeeded()
}
참고 문헌
세부
- Xcode 10.2.1 (10E1001), Swift 5
해결책
import UIKit
extension UIView {
func removeConstraints() { removeConstraints(constraints) }
func deactivateAllConstraints() { NSLayoutConstraint.deactivate(getAllConstraints()) }
func getAllSubviews() -> [UIView] { return UIView.getAllSubviews(view: self) }
func getAllConstraints() -> [NSLayoutConstraint] {
var subviewsConstraints = getAllSubviews().flatMap { $0.constraints }
if let superview = self.superview {
subviewsConstraints += superview.constraints.compactMap { (constraint) -> NSLayoutConstraint? in
if let view = constraint.firstItem as? UIView, view == self { return constraint }
return nil
}
}
return subviewsConstraints + constraints
}
class func getAllSubviews(view: UIView) -> [UIView] {
return view.subviews.flatMap { [$0] + getAllSubviews(view: $0) }
}
}
용법
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
view.deactivateAllConstraints()
다음 방법을 사용하여보기에서 모든 제약 조건을 제거합니다.
.h 파일 :
+ (void)RemoveContraintsFromView:(UIView*)view
removeParentConstraints:(bool)parent
removeChildConstraints:(bool)child;
.m 파일 :
+ (void)RemoveContraintsFromView:(UIView *)view
removeParentConstraints:(bool)parent
removeChildConstraints:(bool)child
{
if (parent) {
// Remove constraints between view and its parent.
UIView *superview = view.superview;
[view removeFromSuperview];
[superview addSubview:view];
}
if (child) {
// Remove constraints between view and its children.
[view removeConstraints:[view constraints]];
}
}
You can also read this post on my blog to better understand how it works behind the hood.
If you need more granular control, I'd strongly advise switching to Masonry, a powerful framework class you could use whenever you need to properly handle constraints programmatically.
A Swift solution:
extension UIView {
func removeAllConstraints() {
var view: UIView? = self
while let currentView = view {
currentView.removeConstraints(currentView.constraints.filter {
return $0.firstItem as? UIView == self || $0.secondItem as? UIView == self
})
view = view?.superview
}
}
}
It's important to go through all the parents, since the constraints between two elements are holds by the common ancestors, so just clearing the superview as detailed in this answer is not good enough, and you might end up having bad surprise later on.
Based on previous answers (swift 4)
You can use immediateConstraints when you don't want to crawl entire hierarchies.
extension UIView {
/**
* Deactivates immediate constraints that target this view (self + superview)
*/
func deactivateImmediateConstraints(){
NSLayoutConstraint.deactivate(self.immediateConstraints)
}
/**
* Deactivates all constrains that target this view
*/
func deactiveAllConstraints(){
NSLayoutConstraint.deactivate(self.allConstraints)
}
/**
* Gets self.constraints + superview?.constraints for this particular view
*/
var immediateConstraints:[NSLayoutConstraint]{
let constraints = self.superview?.constraints.filter{
$0.firstItem as? UIView === self || $0.secondItem as? UIView === self
} ?? []
return self.constraints + constraints
}
/**
* Crawls up superview hierarchy and gets all constraints that affect this view
*/
var allConstraints:[NSLayoutConstraint] {
var view: UIView? = self
var constraints:[NSLayoutConstraint] = []
while let currentView = view {
constraints += currentView.constraints.filter {
return $0.firstItem as? UIView === self || $0.secondItem as? UIView === self
}
view = view?.superview
}
return constraints
}
}
With objectiveC
[self.superview.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
if (constraint.firstItem == self || constraint.secondItem == self) {
[self.superview removeConstraint:constraint];
}
}];
[self removeConstraints:self.constraints];
}
You could use something like this:
[viewA.superview.constraints enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
if (constraint.firstItem == viewA || constraint.secondItem == viewA) {
[viewA.superview removeConstraint:constraint];
}
}];
[viewA removeConstraints:viewA.constraints];
Basically, this is enumerates over all the constraints on the superview of viewA and removes all of the constraints that are related to viewA.
Then, the second part removes the constraints from viewA using the array of viewA's constraints.
(As of July 31, 2017)
SWIFT 3
self.yourCustomView.removeFromSuperview()
self.yourCustomViewParentView.addSubview(self.yourCustomView)
Objective C
[self.yourCustomView removeFromSuperview];
[self.yourCustomViewParentView addSubview:self.yourCustomView];
This is the easiest way to quickly remove all constraints that exist on a UIView. Just be sure to add the UIView back with it's new constraints or new frame afterwards =)
This is the way to disable all constraints from a specific view
NSLayoutConstraint.deactivate(myView.constraints)
참고URL : https://stackoverflow.com/questions/24418884/remove-all-constraints-affecting-a-uiview
'Program Tip' 카테고리의 다른 글
XML에 새 줄 / 바꿈 태그 추가 (0) | 2020.11.03 |
---|---|
ng-options의 키-값 쌍 (0) | 2020.11.03 |
JavaFX FXML 컨트롤러-생성자 대 초기화 방법 (0) | 2020.11.03 |
Linux의 Eclipse에서 패키지 탐색기에서 트리 노드를 확장하기 위해 화살표 키만 사용할 수 있습니까? (0) | 2020.11.03 |
PHP-문자열에 특정 텍스트가 포함되어 있는지 확인하는 방법 (0) | 2020.11.03 |