
第二個螢屏截圖顯示了視圖何時因某種原因跳到左側。我不知道是什么導致了這種情況發生。所有子視圖的 centerX 約束都已停用。

其余的螢屏截圖顯示了其他子視圖的影片回位。但理想情況下,如果您有五個子視圖并且洗掉了中間子視圖,那么該子視圖左側的兩個子視圖將保持原位。只有右側的兩個會設定影片并滑過以填充中間子視圖所在的空間。



問題
- 有誰知道我在影片代碼中做錯了什么?為什么父視圖開始向左跳轉?我只是想滑出正在洗掉的視圖,然后同時滑過已洗掉視圖右側的專案。這就是我想要做的。
代碼
這是我的代碼。影片代碼在removeArrangedSubview:animated:方法中。
我的書架.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, MyShelfItemShape) {
MyShelfItemShapeNone = 0,
MyShelfItemShapeCircular
};
@interface MyShelf : UIView
@property (copy, nonatomic, readonly) NSArray<__kindof UIView *> *arrangedSubviews;
@property (assign, nonatomic) CGSize itemSize;
@property (assign, nonatomic) MyShelfItemShape itemShape;
@property (strong, nonatomic) UIColor *itemBorderColor;
@property (assign, nonatomic) CGFloat itemBorderWidth;
@property (assign, nonatomic) CGFloat preferredMinimumSpacing;
@property (assign, nonatomic) CGFloat preferredMaximumSpacing;
#pragma mark - Managing the Horizontal Order of Arranged Subviews
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex inFront:(BOOL)inFront animated:(BOOL)animated;
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex animated:(BOOL)animated;
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex;
- (void)addArrangedSubview:(UIView *)view inFront:(BOOL)inFront animated:(BOOL)animated;
- (void)addArrangedSubview:(UIView *)view animated:(BOOL)animated;
- (void)addArrangedSubview:(UIView *)view;
- (void)removeArrangedSubview:(UIView *)view animated:(BOOL)animated;
- (void)removeArrangedSubview:(UIView *)view;
#pragma mark - Managing the Vertical Order of Arranged Subviews
- (void)bringArrangedSubviewToFront:(UIView *)view;
@end
NS_ASSUME_NONNULL_END
我的書架
#import "MyShelf.h"
@interface MyShelf ()
@property (strong, nonatomic) UIView *positionView;
@property (strong, nonatomic) UIView *framingView;
@property (strong, nonatomic) NSLayoutConstraint *framingViewTrailingConstraint;
@property (strong, nonatomic, readwrite) NSMutableArray<__kindof UIView *> *mutableArrangedSubviews;
@end
@implementation MyShelf
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self initialize];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initialize];
}
return self;
}
- (void)initialize {
// self.previousFrame = CGRectZero;
// self.spacing = -10;
// self.axis = UILayoutConstraintAxisHorizontal;
// self.alignment = UIStackViewAlignmentCenter;
// self.distribution = UIStackViewDistributionFill;
//self.itemSize = CGSizeZero;
self.itemSize = CGSizeMake(35, 35);
self.itemShape = MyShelfItemShapeNone;
self.itemBorderColor = [UIColor blackColor];
self.itemBorderWidth = 1.0;
self.mutableArrangedSubviews = [[NSMutableArray alloc] init];
self.directionalLayoutMargins = NSDirectionalEdgeInsetsZero;
//framingView will match the bounds of the items and it will look like their superview,
//but it is not the superview of the items
[self addSubview:self.framingView];
//positionView is used for the item position constraints but it is not seen
[self addSubview:self.positionView];
[NSLayoutConstraint activateConstraints:@[
//center the position view vertically with no height
[self.positionView.centerYAnchor constraintEqualToAnchor:self.layoutMarginsGuide.centerYAnchor],
[self.positionView.heightAnchor constraintEqualToConstant:0],
//both the leading and trailing edges of the position view should be inset by 1/2 of the item width
[self.positionView.leadingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor constant:self.itemSize.width / 2.0],
[self.positionView.trailingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.trailingAnchor constant:-self.itemSize.width / 2.0],
//framing view leading is at the positioning view leading mius 1/2 of the item width
[self.framingView.leadingAnchor constraintEqualToAnchor:self.positionView.leadingAnchor constant:-self.itemSize.width / 2.0],
[self.framingView.topAnchor constraintEqualToAnchor:self.layoutMarginsGuide.topAnchor],
[self.framingView.bottomAnchor constraintEqualToAnchor:self.layoutMarginsGuide.bottomAnchor]
]];
}
- (CGSize)intrinsicContentSize {
return CGSizeMake(self.mutableArrangedSubviews.count * self.itemSize.width, self.itemSize.height);
}
- (void)updateHorizontalPositions {
if (self.mutableArrangedSubviews.count == 0) {
//no items, so all we have to do is to update the framing view
self.framingViewTrailingConstraint.active = NO;
self.framingViewTrailingConstraint = [self.framingView.trailingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor];
self.framingViewTrailingConstraint.active = YES;
return;
}
//clear the existing centerX constraints
for (NSLayoutConstraint *constraint in self.positionView.constraints) {
if (constraint.firstAttribute == NSLayoutAttributeCenterX || constraint.firstAttribute == NSLayoutAttributeCenterXWithinMargins) {
constraint.active = NO;
}
}
//the first item will be equal to the positionView's leading
UIView *currentItem = [self.mutableArrangedSubviews firstObject];
[NSLayoutConstraint activateConstraints:@[
[currentItem.centerXAnchor constraintEqualToAnchor:self.positionView.leadingAnchor]
]];
// percentage for remaining item spacing
// examples:
// we have 3 items
// item 0 centerX is at leading
// item 1 centerX is at 50%
// item 2 centerX is at 100%
// we have 4 items
// item 0 centerX is at leading
// item 1 centerX is at 33.33%
// item 2 centerX is at 66.66%
// item 3 centerX is at 100%
CGFloat percent = 1.0 / (CGFloat)(self.mutableArrangedSubviews.count - 1);
UIView *previousItem;
for (int x = 1; x < self.mutableArrangedSubviews.count; x ) {
previousItem = currentItem;
currentItem = self.mutableArrangedSubviews[x];
CGFloat currentPercent = percent * x;
//keep items next to each other (left-aligned) when overlap is not needed
[currentItem.centerXAnchor constraintLessThanOrEqualToAnchor:previousItem.centerXAnchor constant:self.itemSize.width].active = YES;
//centerX as a percentage of the positionView width
//note: this method is being used as opposed to the layout anchor API because the layout anchor API does not support setting the multiplier
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:currentItem
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.positionView
attribute:NSLayoutAttributeTrailing
multiplier:currentPercent
constant:0.0];
//this constraint needs a less-than-required priority so the left-aligned constraint can be enforced
constraint.priority = UILayoutPriorityRequired - 1;
constraint.active = YES;
}
//update the trailing anchor of the framing view to the last shelf item
self.framingViewTrailingConstraint.active = NO;
self.framingViewTrailingConstraint = [self.framingView.trailingAnchor constraintEqualToAnchor:currentItem.trailingAnchor];
self.framingViewTrailingConstraint.active = YES;
}
- (void)addArrangedSubview:(UIView *)view inFront:(BOOL)inFront animated:(BOOL)animated {
[self insertArrangedSubview:view atIndex:self.mutableArrangedSubviews.count inFront:inFront animated:animated];
}
- (void)addArrangedSubview:(UIView *)view animated:(BOOL)animated {
[self addArrangedSubview:view inFront:NO animated:animated];
}
- (void)addArrangedSubview:(UIView *)view {
[self addArrangedSubview:view animated:NO];
}
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex inFront:(BOOL)inFront animated:(BOOL)animated {
CGFloat height = MAX(view.bounds.size.height, view.bounds.size.width);
//if the itemSize is CGSizeZero, then that means to use the size of the provided views
if (!CGSizeEqualToSize(self.itemSize, CGSizeZero)) {
[NSLayoutConstraint activateConstraints:@[
[view.widthAnchor constraintEqualToConstant:self.itemSize.width],
[view.heightAnchor constraintEqualToConstant:self.itemSize.height]
]];
height = MAX(self.itemSize.height, self.itemSize.width);
}
switch (self.itemShape) {
case MyShelfItemShapeNone:
break;
case MyShelfItemShapeCircular:
view.layer.cornerRadius = height / 2.0;
break;
}
view.layer.borderColor = self.itemBorderColor.CGColor;
view.layer.borderWidth = self.itemBorderWidth;
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.mutableArrangedSubviews insertObject:view atIndex:stackIndex];
if (inFront) {
[self.positionView addSubview:view];
} else {
//insert the view as a subview of positionView at index zero so it will be underneath existing items
[self.positionView insertSubview:view atIndex:0];
}
[NSLayoutConstraint activateConstraints:@[
[view.centerYAnchor constraintEqualToAnchor:self.positionView.centerYAnchor]
]];
[self invalidateIntrinsicContentSize];
[self updateHorizontalPositions];
[self updateVerticalPositions];
}
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex animated:(BOOL)animated {
[self insertArrangedSubview:view atIndex:stackIndex inFront:NO animated:animated];
}
- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex {
[self insertArrangedSubview:view atIndex:stackIndex animated:NO];
}
- (void)removeArrangedSubview:(UIView *)view animated:(BOOL)animated {
BOOL wasInFront = NO;
if ([self.positionView.subviews lastObject] == view) {
wasInFront = YES;
}
if (animated) {
[self.mutableArrangedSubviews removeObject:view];
[self invalidateIntrinsicContentSize];
//clear the existing centerX constraints
// for (NSLayoutConstraint *constraint in self.positionView.constraints) {
// if (constraint.firstAttribute == NSLayoutAttributeCenterX || constraint.firstAttribute == NSLayoutAttributeCenterXWithinMargins) {
// constraint.active = NO;
// }
// }
[self layoutIfNeeded];
__weak MyShelf *weakSelf = self;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionAllowUserInteraction
animations:^{
view.alpha = 0.0;
view.center = CGPointMake(-weakSelf.itemSize.width, view.center.y);
[weakSelf updateHorizontalPositions];
[weakSelf layoutIfNeeded];
}
completion:^(BOOL finished){
[view removeFromSuperview];
//only reorder the views vertically if the one being removed was the top-most view
if (wasInFront) {
[weakSelf updateVerticalPositions];
}
}];
} else {
[view removeFromSuperview];
[self.mutableArrangedSubviews removeObject:view];
[self invalidateIntrinsicContentSize];
[self updateHorizontalPositions];
//only reorder the views verticall if the one being removed was the top-most view
if (wasInFront) {
[self updateVerticalPositions];
}
}
}
- (void)removeArrangedSubview:(UIView *)view {
[self removeArrangedSubview:view animated:NO];
}
- (NSArray<__kindof UIView *> *)arrangedSubviews {
return self.mutableArrangedSubviews;
}
#pragma mark - Managing the Vertical Order of Arranged Subviews
- (void)updateVerticalPositions {
if (!self.positionView.subviews.count) {
return;
}
//get the view that is on the top and find out what horizontal position it is in
UIView *topView = [self.positionView.subviews lastObject];
NSUInteger horizontalIndex = [self.mutableArrangedSubviews indexOfObject:topView];
for (NSInteger x = horizontalIndex - 1; x >= 0; x--) {
UIView *view = self.mutableArrangedSubviews[x];
[self.positionView sendSubviewToBack:view];
}
for (NSInteger x = horizontalIndex 1; x < self.mutableArrangedSubviews.count; x ) {
UIView *view = self.mutableArrangedSubviews[x];
[self.positionView sendSubviewToBack:view];
}
}
- (void)bringArrangedSubviewToFront:(UIView *)view {
[self.positionView bringSubviewToFront:view];
[self updateVerticalPositions];
}
- (UIView *)framingView {
if (!self->_framingView) {
self->_framingView = [[UIView alloc] init];
self->_framingView.translatesAutoresizingMaskIntoConstraints = NO;
self->_framingView.backgroundColor = [UIColor systemYellowColor];
}
return self->_framingView;
}
- (UIView *)positionView {
if (!self->_positionView) {
self->_positionView = [[UIView alloc] init];
self->_positionView.translatesAutoresizingMaskIntoConstraints = NO;
self->_positionView.backgroundColor = nil;
}
return self->_positionView;
}
- (NSLayoutConstraint *)framingViewTrailingConstraint {
if (!self->_framingViewTrailingConstraint) {
self->_framingViewTrailingConstraint = [self.framingView.trailingAnchor constraintEqualToAnchor:self.positionView.leadingAnchor];
self->_framingViewTrailingConstraint.priority = UILayoutPriorityRequired;
}
return self->_framingViewTrailingConstraint;
}
@end
uj5u.com熱心網友回復:
我不是蘋果工程師,所以我不知道其中的來龍去脈,但我經常看到它。
作為一般規則......當影片約束時,我們希望允許自動布局從“自上而下”的角度管理視圖層次結構。
如果您更改約束并將子視圖告知layoutIfNeeded,則似乎superview要么不知道發生了什么,要么時間會導致問題。
如果您將影片塊更改為[weakSelf.superview layoutIfNeeded];應該可以解決“跳躍”問題:
__weak MyShelf *weakSelf = self;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionAllowUserInteraction
animations:^{
view.alpha = 0.0;
view.center = CGPointMake(-weakSelf.itemSize.width, view.center.y);
[weakSelf updateHorizontalPositions];
// tell the superview to initiate auto-layout updates
//[weakSelf layoutIfNeeded];
[weakSelf.superview layoutIfNeeded];
} completion:^(BOOL finished){
[view removeFromSuperview];
//only reorder the views vertically if the one being removed was the top-most view
if (wasInFront) {
[weakSelf updateVerticalPositions];
}
}];
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/481868.html
上一篇:在樣式化組件中使用影片
