뷰가 숨겨져 있을 때 자동 레이아웃을 사용하여 다른 뷰를 이동하는 방법은 무엇입니까?
IB에서 커스텀 셀을 설계하고 서브클래스를 분류하여 커스텀클래스에 콘센트를 연결했습니다.셀 콘텐츠에는 UIView(cdView)와 라벨(titleLabel과 emailLabel)의 3개의 서브뷰가 있습니다.각 행에 사용할 수 있는 데이터에 따라 셀에 UIView와 2개의 라벨을 표시하고 싶을 때도 있고 2개의 라벨만 표시할 때도 있습니다.UIView 속성을 hidden으로 설정하거나 슈퍼뷰에서 삭제하면 두 개의 라벨이 왼쪽으로 이동합니다.UIView 선행제약을 10px의 경우 Superview(Cell content)로, 10px의 경우 UILabels 선행제약을 다음 뷰(UIView)로 설정하려고 했습니다.코드의 후반부
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
// ...
Record *record = [self.records objectAtIndex:indexPath.row];
if ([record.imageURL is equalToString:@""]) {
cell.cdView.hidden = YES;
}
}
cdView를 숨기고 있는데, 라벨은 Cell에서 같은 위치에 있지만 왼쪽으로 이동해 주셨으면 합니다.superview에서 cell.cdView를 삭제하려고 했지만 역시 작동하지 않았습니다.제가 무엇을 하고 있는지 명확히 하기 위해 이미지를 첨부했습니다.
나는 이것을 프로그램적으로 하는 방법을 알고 있고, 그 해결책을 찾고 있지 않다.내가 원하는 것은 IB에서 제약을 설정하는 것이며 다른 뷰가 제거되거나 숨겨지면 내 서브뷰가 동적으로 이동하기를 기대합니다.자동 레이아웃으로 IB에서 할 수 있나요?
.....
가능하지만, 당신은 약간의 추가 작업을 해야 할 것입니다.먼저 몇 가지 개념적인 문제를 해결해야 합니다.
- 숨겨진 보기는 그리지는 않지만 자동 레이아웃에 계속 참여하며 일반적으로 프레임이 유지되고 다른 관련 보기는 해당 위치에 남아 있습니다.
- 뷰의 슈퍼뷰에서 뷰를 제거하면 관련된 모든 제약조건도 해당 뷰 계층에서 제거됩니다.
당신의 경우, 이것은 다음을 의미합니다.
- 왼쪽 보기를 숨기도록 설정하면 레이블은 그대로 유지됩니다. 왼쪽 보기가 보이지 않더라도 공간을 차지하기 때문입니다.
- 왼쪽 보기를 제거하면 레이블의 왼쪽 모서리에 대한 구속이 없어지므로 레이블이 모호하게 구속된 상태로 남아 있을 수 있습니다.
당신이 해야 할 일은 당신의 라벨을 현명하게 과도하게 구속하는 것입니다.기존 제약 조건(다른 뷰에 10포인트 공간)은 그대로 두되, 다른 제약 조건을 추가합니다. 즉, 라벨의 왼쪽 가장자리를 필요하지 않은 우선 순위(기본 높은 우선 순위는 잘 작동합니다.)로 슈퍼뷰의 왼쪽 가장자리에서 10포인트 떨어져 있어야 합니다.
그런 다음 왼쪽 보기를 모두 제거합니다.왼쪽 뷰에 대한 필수 10pt 제약조건은 관련된 뷰와 함께 사라지며 라벨이 슈퍼뷰에서 10포인트 떨어져 있는 높은 우선순위의 제약조건만 남게 됩니다.다음 배치 경로에서는 가장자리를 중심으로 간격을 두고 슈퍼뷰의 너비를 채울 때까지 왼쪽으로 확장됩니다.
한 가지 중요한 경고는 왼쪽 뷰를 그림으로 되돌리려면 뷰 계층에 추가해야 할 뿐만 아니라 모든 제약을 동시에 재정립해야 한다는 것입니다.즉, 뷰가 다시 표시될 때마다 뷰와 라벨 사이에 10pt 간격 제약을 다시 넣을 수 있는 방법이 필요합니다.
런타임 중에 제약 조건을 추가하거나 제거하는 것은 성능에 영향을 줄 수 있는 무거운 작업입니다.하지만, 더 간단한 대안이 있습니다.
숨기려는 뷰에 대해 너비 구속조건을 설정합니다.수평 간격이 있는 다른 뷰를 해당 뷰로 구속합니다.
, 「」를 합니다..constant
.0.f로 합니다.다른 보기는 자동으로 왼쪽으로 이동하여 위치를 차지합니다.
상세한 것에 대하여는, 다음의 회답을 참조해 주세요.
iOS 8+만 지원하는 사용자에게는 새로운 부울 속성이 활성화되어 있습니다.필요한 제약만을 동적으로 활성화하는 데 도움이 됩니다.
추신. 구속 콘센트는 약하지 않고 강해야 합니다.
§:
@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!
func showView() {
optionalView.isHidden = false
viewIsVisibleConstraint.isActive = true
viewIsHiddenConstraint.isActive = false
}
func hideView() {
optionalView.isHidden = true
viewIsVisibleConstraint.isActive = false
viewIsHiddenConstraint.isActive = true
}
는 선택을 Installed
체크박스를 켜겠습니다.
UIStack View (iOS 9+)
는 자신의 입니다.UIStackView
가 UIStackView
됩니다.
UIStackView
이 경우 뷰는 자동으로 재배치됩니다.hidden
(iOS 9+) 중중i)))))))))) 。
UIView.animateWithDuration(1.0) { () -> Void in
self.mySubview.hidden = !self.mySubview.hidden
}
데모를 보려면 이 WWDC 비디오에서 11:48로 이동하십시오.
인 것을 하고 있습니다.@IBDesignable
의 서브 입니다.UILabel
등의 하기 위해) 것을 (색, 글꼴, 삽입 등
override func intrinsicContentSize() -> CGSize {
if hidden {
return CGSizeZero
} else {
return super.intrinsicContentSize()
}
}
이렇게 하면 레이블 하위 클래스가 자동 레이아웃에 참여할 수 있지만 숨길 때는 공간을 차지하지 않습니다.
구글의 경우: 맥스의 답변을 바탕으로 많은 사람들이 눈치채고 있는 패딩 문제를 해결하기 위해 라벨의 높이를 높여서 실제 패딩이 아닌 구분 기호로 사용했습니다.이 아이디어는 뷰를 포함하는 모든 시나리오에 대해 확장될 수 있습니다.
다음은 간단한 예입니다.
이 경우 작성자 레이블의 높이를 적절한 위치에 매핑합니다.IBOutlet
:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
가 구속의 를 and리로 했을 때0.0f
재생 버튼의 높이로 사용할 수 있기 때문에, 「패딩」을 보존하고 있습니다.
uiview와 레이블 사이의 제약 조건을 IBOutlet으로 연결하고 숨김 설정 시 우선 순위 멤버를 더 작은 값으로 설정합니다. = YES
결국 2개의 xib를 만들었습니다.하나는 왼쪽이 있고 다른 하나는 왼쪽이 없습니다.컨트롤러에 둘 다 등록하고 cellForRowAt에서 어떤 것을 사용할지 결정하였습니다.IndexPath.
이들은 동일한 UITableViewCell 클래스를 사용합니다.단점은 xibs 사이에 콘텐츠의 중복이 있다는 것입니다만, 이러한 셀은 매우 기본적인 것입니다.장점은 뷰 삭제, 제약조건 업데이트 등을 수동으로 관리할 수 있는 코드가 많이 없다는 것입니다.
일반적으로는 레이아웃이 기술적으로 다르므로 xib가 달라야 하므로 이 방법이 더 나을 수 있습니다.
[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];
TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];
이 경우 작성자 라벨의 높이를 적절한 IBOutlet에 매핑합니다.
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
그리고 구속의 높이를 0.0f로 설정하면 재생 버튼의 높이를 사용할 수 있기 때문에 패딩이 유지됩니다.
cell.authorLabelHeight.constant = 0;
2개의 UIStack View 수평 및 수직을 사용합니다.스택 내의 일부 서브뷰가 숨겨져 있는 경우 다른 스택서브뷰가 이동됩니다.2개의 UILabel을 사용하여 수직 스택의 경우 분산 -> 프로포럴하게 채우기(Fill Proporaly for Vertical stack for Vertical stack)를 사용합니다.첫 번째 UIView의 폭과 높이를 설정해야 합니다.
UIStack View를 사용하면 모든 것이 정상적으로 동작합니다.UIStackView는 다른 제약을 걱정할 필요 없이 자동으로 공간을 처리합니다.
이 특정 레이아웃에서 작업해야 하는 제약조건은 숨겨진 뷰의 '선행' 제약조건입니다.하지만 아래 이론은 모든 방향으로 적용될 것입니다.
1: 모든 보기가 표시될 때 원하는 방식으로 모든 구속조건을 설정합니다.
2: 숨길 보기에 두 번째 '선행' 제약 조건을 추가합니다.이렇게 하면 잠시 동안 제약이 풀립니다.
3: 원래 선행 제약 조건의 우선 순위를 '999'로 변경합니다. 그러면 1000이 되고 더 이상 제약 조건이 깨지지 않습니다.
4: 새로운 제약 조건을 'leading=leading'에서 'leading=leading'으로 변경합니다.숨기려는 뷰를 부모 뷰의 앞쪽 가장자리에서 이동시킵니다.
5: 새로운 제약조건의 isActive 값을 전환하면 뷰 또는 뷰 외부에 있는 경우 전환됩니다.가시성을 true/false로 설정하는 동시에 true/false로 설정합니다.예:
@IBOutlet var avatar:UIImage!
@IBOutlet var avatarLeadHid:NSLayoutConstraint!
func hideAvatar() {
self.avatar.isHidden = true
self.avatarLeadHid.isActive = true
}
func showAvatar() {
self.avatar.isHidden = false
self.avatarLeadHid.isActive = false
}
보너스: 뷰를 숨길 때 사용할 패딩/마진을 변경하기 위해 새로운 hider-constraint의 '상수' 값을 조정할 수 있습니다.이 값은 음수일 수 있습니다.
추가 보너스:Hider-constraint의 'Installed' 체크박스를 켜는 것만으로 코드를 실행하지 않고도 Interface Builder 내에서 레이아웃을 볼 수 있습니다.
기타 도움말: 포인트 리스트보다 내가 더 잘하는 것을 보여주는 비디오를 만들었습니다.https://youtu.be/3tGEwqtQ-iU
내 경우 높이 구속의 상수를 다음과 같이 설정합니다.0.0f
and을 합니다.hidden
을 property property로 설정합니다.YES
.
(와 함께) (서브뷰와 ) 보다.하고 0을 합니다.hidden
을 property property로 설정합니다.NO
.
한번 해봐, 내가 아래 코드를 구현했어.
다른 3개의 뷰를 추가한 ViewController의 View가 1개 있습니다.보기가 숨겨지면 다른 2개의 뷰가 이동합니다.다음 절차를 따릅니다.
1. ViewController.h 파일
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end
2. View Controller.m
#import "ViewController.h"
@interface ViewController ()
{
CGFloat viewOneWidthConstant;
CGFloat viewTwoWidthConstant;
CGFloat viewThreeWidthConstant;
CGFloat viewBottomWidthConstant;
}
@end
@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a
nib.
/*
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
*/
// [viewOne setHidden:NO];
// [viewTwo setHidden:NO];
// [viewThree setHidden:NO];
// [viewOne setHidden:NO];
// [viewTwo setHidden:NO];
// [viewThree setHidden:YES];
// [viewOne setHidden:NO];
// [viewTwo setHidden:YES];
// [viewThree setHidden:NO];
// [viewOne setHidden:NO];
// [viewTwo setHidden:YES];
// [viewThree setHidden:YES];
// [viewOne setHidden:YES];
// [viewTwo setHidden:NO];
// [viewThree setHidden:NO];
// [viewOne setHidden:YES];
// [viewTwo setHidden:NO];
// [viewThree setHidden:YES];
// [viewOne setHidden:YES];
// [viewTwo setHidden:YES];
// [viewThree setHidden:NO];
// [viewOne setHidden:YES];
// [viewTwo setHidden:YES];
// [viewThree setHidden:YES];
[self hideShowBottomBar];
}
- (void)hideShowBottomBar
{
BOOL isOne = !viewOne.isHidden;
BOOL isTwo = !viewTwo.isHidden;
BOOL isThree = !viewThree.isHidden;
viewOneWidthConstant = _viewOneWidth.constant;
viewTwoWidthConstant = _viewTwoWidth.constant;
viewThreeWidthConstant = _viewThreeWidth.constant;
viewBottomWidthConstant = _viewBottomWidth.constant;
if (isOne && isTwo && isThree) {
// 0 0 0
_viewOneWidth.constant = viewBottomWidthConstant / 3;
_viewTwoWidth.constant = viewBottomWidthConstant / 3;
_viewThreeWidth.constant = viewBottomWidthConstant / 3;
}
else if (isOne && isTwo && !isThree) {
// 0 0 1
_viewOneWidth.constant = viewBottomWidthConstant / 2;
_viewTwoWidth.constant = viewBottomWidthConstant / 2;
_viewThreeWidth.constant = 0;
}
else if (isOne && !isTwo && isThree) {
// 0 1 0
_viewOneWidth.constant = viewBottomWidthConstant / 2;
_viewTwoWidth.constant = 0;
_viewThreeWidth.constant = viewBottomWidthConstant / 2;
}
else if (isOne && !isTwo && !isThree) {
// 0 1 1
_viewOneWidth.constant = viewBottomWidthConstant;
_viewTwoWidth.constant = 0;
_viewThreeWidth.constant = 0;
}
else if (!isOne && isTwo && isThree) {
// 1 0 0
_viewOneWidth.constant = 0;
_viewTwoWidth.constant = viewBottomWidthConstant / 2;
_viewThreeWidth.constant = viewBottomWidthConstant / 2;
}
else if (!isOne && isTwo && !isThree) {
// 1 0 1
_viewOneWidth.constant = 0;
_viewTwoWidth.constant = viewBottomWidthConstant;
_viewThreeWidth.constant = 0;
}
else if (!isOne && !isTwo && isThree) {
// 1 1 0
_viewOneWidth.constant = 0;
_viewTwoWidth.constant = 0;
_viewThreeWidth.constant = viewBottomWidthConstant;
}
else if (isOne && isTwo && isThree) {
// 1 1 1
_viewOneWidth.constant = 0;
_viewTwoWidth.constant = 0;
_viewThreeWidth.constant = 0;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
그래서 이 논리가 누군가를 도울 수 있기를 바랍니다.
가로 스택뷰를 사용합니다.서브뷰가 숨겨지면 프레임을 삭제할 수 있습니다.
아래 그림에서 빨간색 뷰는 콘텐츠의 실제 컨테이너이며 오렌지색 슈퍼뷰(ShowHideView)까지 10pt의 후행 공간이 있습니다. 그런 다음 ShowHideView를 IBOutlet에 연결하고 프로그램적으로 표시/숨김/제거하기만 하면 됩니다.
- 이것은 뷰가 표시/설치되는 경우입니다.
- 이 경우 뷰가 숨겨지거나 설치되지 않습니다.
이것은 우선 순위 제약을 사용하는 또 다른 솔루션입니다.아이디어는 폭을 0으로 설정합니다.
콘텐츠 뷰 작성(빨간색) 및 후행 공간 10pt를 슈퍼뷰로 설정합니다(표시).후행 공간 제약에 주목하십시오. 우선 순위가 다른 후행 제약 조건이 2개 있습니다.낮음(=10) 및 높음(<=10).이것은 애매모호함을 피하기 위해 중요합니다.
가장 쉬운 솔루션은 UIStack View(수평)를 사용하는 것입니다.스택 뷰에 추가: 첫 번째 뷰와 레이블이 있는 두 번째 뷰.그런 다음 첫 번째 뷰의 isHidden 속성을 false로 설정합니다.모든 구속조건이 자동으로 계산되고 업데이트됩니다.
보기를 숨기는 대신 너비 제한을 만들고 UIView를 숨기려면 코드 내에서 0으로 변경합니다.
그렇게 하는 것이 가장 간단한 방법일지도 모른다.또한 보기를 보존하고 다시 표시하려면 보기를 다시 작성할 필요가 없습니다(표 셀 내부 사용).상수 값을 변경하려면 뷰의 아웃렛과 같은 방식으로 상수 참조 아웃렛을 작성해야 합니다.
no_scene이 제안했듯이 실행 시 제약조건의 우선순위를 변경함으로써 확실하게 이를 수행할 수 있습니다.삭제해야 할 차단 뷰가 여러 개 있었기 때문에 이 작업은 훨씬 쉬웠습니다.
다음은 Reactive Cocoa를 사용한 토막입니다.
RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;
RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
combineLatest:@[isViewOneHiddenSignal,
isViewTwoHiddenSignal,
isViewThreeHiddenSignal]]
and]
distinctUntilChanged]
map:^id(NSNumber* allAreHidden) {
return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
}];
RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
subscribeNext:^(id x) {
@strongify(self);
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.3 animations:^{
[self.view layoutIfNeeded];
}];
}];
이것이 누군가에게 도움이 될까 봐 시각 형식 제약을 사용하는 도우미 클래스를 만들었습니다.현재 앱에서 사용하고 있습니다.
제 필요에 따라 조금 조정될 수도 있지만, 도움이 될 수도 있고, 수정하여 자신만의 도우미를 만들 수도 있습니다.
위의 답변, UIScroll View에 대한 답변 및 이 튜토리얼에 대해 Tim에게 감사해야 합니다.
솔루션을 입수하기 위해서, uiview를 재조정하는 방법은 다음과 같습니다.
- 1개의 UIMageView를 드래그 드롭하여 왼쪽에 배치합니다.
- UIView를 1개 드래그 드롭하여 UIMageView 오른쪽에 배치합니다.
- 선행 및 후행 제약 조건이 0인 UIView 내에 2개의 UILabel을 드래그 드롭합니다.
- UIMagView 대신 2개의 라벨이 포함된 UIView의 선행 구속조건을 슈퍼뷰로 설정합니다.
- UIImageView가 숨겨져 있는 경우 선행 제약 상수를 10px로 설정하여 슈퍼뷰합니다.그렇지 않으면 선행 제약 상수를 10px + UIImageView로 설정합니다.폭 + 10px
나는 나만의 엄지 규칙을 만들었다.제약 조건이 영향을 받을 수 있는 모든 uiview를 숨기거나 표시해야 할 때마다 uiview 내에 영향을 받는 모든 종속 하위 뷰를 추가하고 선행/후행/상단/하단 제약 조건을 프로그래밍 방식으로 업데이트합니다.
이것은 오래된 질문이지만 여전히 도움이 되길 바란다.Android에서 나온 이 플랫폼에서는 편리한 방법을 사용할 수 있습니다.isVisible
뷰에서 숨길 뿐만 아니라 자동 레이아웃이 뷰를 그릴 때 프레임이 고려되지 않습니다.
extension 및 extend uiview를 사용하면 ios에서도 같은 기능을 실행할 수 있습니다(UIKit에는 아직 없는 이유는 확실하지 않습니다).여기서 swift 3의 실장을 실행할 수 있습니다.
func isVisible(_ isVisible: Bool) {
self.isHidden = !isVisible
self.translatesAutoresizingMaskIntoConstraints = isVisible
if isVisible { //if visible we remove the hight constraint
if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){
self.removeConstraint(constraint)
}
} else { //if not visible we add a constraint to force the view to have a hight set to 0
let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
self.addConstraint(height)
}
self.layoutIfNeeded()
}
올바른 방법은 isActive = false를 사용하여 제약을 사용하지 않도록 설정하는 것입니다.단, 제약조건을 비활성화하면 해당 제약조건이 삭제 및 해제되므로 강력한 콘센트가 필요합니다.
이게 가장 간단한 답인 것 같아요.동작하고 있는 것을 확인해 주세요.
StackFullView.layer.isHidden = true
Task_TopSpaceSections.constant = 0. //your constraint of top view
https://www.youtube.com/watch?v=EBulMWMoFuw 를 확인해 주세요.
언급URL : https://stackoverflow.com/questions/18065938/how-to-use-auto-layout-to-move-other-views-when-a-view-is-hidden
'IT' 카테고리의 다른 글
T-SQL을 사용하여 두 정수 값을 나누어 부동 결과를 얻는 방법은 무엇입니까? (0) | 2023.04.23 |
---|---|
코코아 앱에서 터미널 명령 실행 (0) | 2023.04.23 |
SQL에서 조회할 매개 변수를 전달하는 방법(Excel) (0) | 2023.04.23 |
T-SQL에 해당하는 분할 함수? (0) | 2023.04.23 |
두 데이터베이스 간의 외부 키 관계 추가 (0) | 2023.04.23 |