【问题标题】:how to reposition dynamic UIViews (with fixed size) with orientation change on iPad如何在 iPad 上通过方向更改重新定位动态 UIView(具有固定大小)
【发布时间】:2012-04-09 02:13:57
【问题描述】:

我正在构建一个 iPad 应用程序,其主屏幕由一个 UIScrollView 组成,其中填充了动态数量的固定大小的 UIView。我目前正在以编程方式创建 UIView,并通过在添加到滚动视图之前设置中心(setCenter)来手动放置它们。该功能看起来很像 iPad 上 Pages 的打开屏幕的简化版本。当每一行都充满 UIViews 时,我正在以编程方式更改滚动视图容器的垂直长度。我必须手动放置每个新创建的 UIView,因为数量将由用户创建的对象数量动态确定。

由于我还想允许方向更改,因此我使用通知中心来捕获方向更改事件。无论方向如何,我都希望各个 UIView 保持相同的大小,但需要通过方向更改对它们重新排序,因为纵向每行会少一个 UIView。

我认为在滚动视图中重新排序 UIView 的唯一方法是以编程方式更改每个 UIView 中心 (setCenter),将它们包装在动画中。

除了遍历子视图和调整它们的中心位置之外,还有其他方法可以重新排序我没有考虑的 UIViews 吗?

谢谢!

【问题讨论】:

    标签: xcode uiview orientation


    【解决方案1】:

    当您有一个视图需要以特定方式布置其子视图时,一般的做法是创建您自己的视图子类并覆盖其layoutSubviews 方法。系统会在不同时间自动向视图发送layoutSubviews,包括视图大小发生变化时。如果系统正在自动旋转,它会从自动旋转动画块内向您发送layoutSubviews,因此您无需担心对子视图帧的更改进行动画处理。

    当您的视图是滚动视图时,这会有点棘手,因为UIScrollView 会向自己发送很多layoutSubview 消息 - 每次它都会更改它contentOffset。所以如果你继承UIScrollView,你想要高效。您需要跟踪上次布置的尺寸,并且只有在尺寸发生变化时才重新布置。像这样的:

    @implementation MyScrollView {
        CGSize _lastLayoutSize;
    }
    
    - (void)forceLayout {
        _lastLayoutSize = CGSizeMake(-1, -1);
        [self setNeedsLayout];
    }
    
    - (void)didAddSubview:(UIView *)view {
        // UIView sends itself this whenever a subview is added.
        [super didAddSubview:view];
        [self forceLayout];
    }
    
    - (void)didRemoveSubview:(UIView *)view {
        // UIView sends itself this whenever a subview is removed.
        [super didAddSubview:view];
        [self setNeedsLayout];
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
    
        CGSize mySize = self.bounds.size;
        if (CGSizeEqualToSize(mySize, _lastLayoutSize))
            return;
        _lastLayoutSize = mySize;
    
        static CGFloat const margin = 8;
        CGFloat yMax = 0;
        CGPoint point = CGPointMake(margin, margin);
        BOOL rowIsEmpty = YES;
        for (UIView *subview in self.subviews) {
            CGRect frame = subview.frame;
            CGFloat xNext = point.x + frame.size.width + margin;
            if (rowIsEmpty || xNext <= mySize.width) {
                rowIsEmpty = NO;
                point.x = xNext;
            } else {
                point.x = margin;
                point.y = yMax + margin;
            }
            frame.origin = point;
            subview.frame = frame;
            yMax = MAX(yMax, CGRectGetMaxY(frame));        
        }
    
        self.contentSize = CGSizeMake(mySize.width, yMax + margin);
    }
    

    【讨论】:

    • 我明白你的意思是,当你滚动时,scrollView 会产生大量对 layoutSubviews 的调用。感谢上面的代码让我开始。稍后阅读此内容的任何人都需要注意,xNext 的计算应该在原点设置之后进行,在 yMax 值旁边。再次感谢 Rob。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    相关资源
    最近更新 更多