菜鸟教程小白 发表于 2022-12-13 09:02:43

ios - ScrollView和ImageView-多次旋转设备后图像未居中


                                            <p>我的应用程序中有一个带有三个视图的segmentedControl,其中一个是scrollView,它的工作原理类似于一种没有缩放的画廊,其<code>pageControl</code>和<code>imageView</code>居中。<br><br>层次结构就像<br><br>->分段控件(3个视图):descriptionView,imageTabView,shareView<br>----> imagesTabView(UIView)<br>------> scrollView<br>------> imageView<br>----> pageControl<br><br>当设备是纵向或横向时,imageView图像会正确显示,它们将居中,并且滚动效果非常好。<br><br>唯一的问题是,当您再次打开设备时,如果图像“在中间”(例如3的第2或6的第3),则显示为偏心,向左或向右移动,并稍微滑动一下它返回到中心,而如果图像是第一个或最后一个,则它可以正常工作。<br><br>我在S.O.上看过这里在各种线程上,尝试将<code>contentView</code>设置为<code>scrollView</code>的子视图,然后将<code>imageView</code>添加为<code>contentView</code>的子视图,但是没有用,尝试将imageView附加到scrollView的底部或右侧,但没有用。<br><br>我觉得我距离实现自己的目标还很远,唯一的问题是我无法理解为什么它没有居中。<br><br>在<code>viewWillLayoutSubviews</code>中,我指定了<code>contentSize</code>,以便在旋转时正确设置其大小,例如<br><pre><code>-(void)viewWillLayoutSubviews{
    ;
    self.scrollView.contentSize = CGSizeMake (self.scrollView.frame.size.width * photosArray.count, 1);
}
</code></pre><br>这是初始化pageControl,scrollView和imageView的方法:<br><pre><code>-(void)configureImageTab{
    pageControl = ;
    ;
    pageControl.translatesAutoresizingMaskIntoConstraints = NO;

    //Don&#39;t show pageControl when there are no photos
    if (photosURL.count == 0)
      pageControl.hidden = YES;

    //Configuring scrollView
    self.scrollView = [ initWithFrame:CGRectMake(0, 0, self.imageSegmentView.frame.size.width, self.imageSegmentView.frame.size.height-pageControl.frame.size.height)];
    self.scrollView.pagingEnabled = YES;
    self.scrollView.delegate = self;
    self.scrollView.userInteractionEnabled = YES;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;

    //... Code cut - adding remote images to fetch to array

    //Actual setup -&gt; scrollView adding imageView as subview with all the images
    for (int i =0; i&lt; photosArray.count; i++){
      CGRect frame;
      frame.origin.x = self.scrollView.frame.size.width * i;
      frame.origin.y = 0;
      frame.size = self.scrollView.frame.size;

      //imageView setup
      imageView = [initWithFrame:frame];
      imageView.backgroundColor = ;
      imageView.clipsToBounds = YES;
      imageView.userInteractionEnabled = YES;
      imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
      imageView.translatesAutoresizingMaskIntoConstraints = YES;
      imageView.contentMode = UIViewContentModeScaleAspectFit;

      //Setting images urls
      ] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
      //Error handling
            }
      }usingActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];

      //Adding gesture recognizer to scrollView and imageView as subview
      ;
      ;
    }

    //Setting the contentSize
    pageControl.numberOfPages = ;

    ;
    ;

    //Constraints
    NSDictionary *views = @{@&#34;pageControl&#34; : pageControl, @&#34;scrollView&#34; : self.scrollView};

    -0-|&#34; options:0 metrics:nil views:views]];
    -1--1-|&#34; options:0 metrics:nil views:views]];
    |&#34; options:0 metrics:nil views:views]];

    ];
}

#pragma mark - scrollView delegate -

-(void)scrollViewDidScroll:(UIScrollView *)sView{
    CGFloat pageWidth = self.scrollView.frame.size.width;
    int page = floor ((self.scrollView.contentOffset.x - pageWidth /2) /pageWidth) +1;
    self.pageControl.currentPage = page;
}

-(IBAction)changePage {
    CGRect frame;
    frame.origin.x = self.scrollView.frame.size.width * self.pageControl.currentPage;
    frame.origin.y = 0;
    frame.size = self.scrollView.frame.size;
    ;
}

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    pageControlBeingUsed = NO;
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    pageControlBeingUsed = NO;
}
</code></pre><br>需要说明的一点是:imageView使用的是autoresizingMask:如果不这样做,它将无法正确显示图像。<br><br>我的猜测是,scrollView委托中可能需要修复一些问题,但我不太确定。<br><br>任何建议表示赞赏!<br><br>编辑<br><br>我注意到在浏览用户的图片然后打开设备时,Twitter应用程序中也会发生相同的错误。<br><br> <strong>编辑TL; DR的2 </strong><br><br>基本上,假设我在带有分页的水平scrollView中有3张图像。<br><br>我在第一张照片上将设备从“肖像”转换为“风景”,并在正确位置居中显示在自己的位置。<br><br>我移到下一张照片,居中显示,然后再次将设备转到肖像。照片未正确对齐,未居中<br><br>实际上,当设备旋转多次时,第一个和最后一个图像会居中显示。其他不居中<br><br> <strong>编辑3 </strong><br><br>我提取了一些行,并做了一个<a href="https://dl.dropboxusercontent.com/u/20375801/OrientationBug.zip" rel="noreferrer noopener nofollow">sample project</a>来演示我遇到的问题。我想contentSize肯定有问题。</p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p>我们可以通过在界面将要旋转时记录当前页面,然后在系统旋转后适当地设置滚动视图的<code>contentOffset</code>来解决您正在谈论的特定错误(滚动视图在滚动视图后未对齐页面边界)已更新滚动视图的边界大小。让我们添加一个<code>pageNumberPriorToRotation</code>实例变量:<br><pre><code>@implementation ViewController {
    CGFloat pageNumberPriorToRotation;
}
</code></pre><br>然后,在界面即将旋转时进行设置:<br><pre><code>- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    ;
    ;
}

- (void)setPageNumberPriorToRotation {
    CGRect bounds = self.scrollView.bounds;
    static const int kNumberOfImages = 3;
    pageNumberPriorToRotation = fmin(round(bounds.origin.x / bounds.size.width),
      kNumberOfImages - 1);
}
</code></pre><br>我们使用它来设置界面旋转期间滚动视图的<code>contentOffset</code>:<br><pre><code>-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    ;
    ;
}

- (void)updateScrollViewLayout {
    CGRect bounds = self.scrollView.bounds;
    bounds.origin.x = bounds.size.width * pageNumberPriorToRotation;
    self.scrollView.bounds = bounds;
}
</code></pre><br>这可以解决您的主要抱怨:旋转后,滚动视图将始终与页面视图边界对齐。<br><br>然而…<br><br>滚动视图交互还存在其他一些问题。在横向方向上,我无法滚动到第三张图像。旋转到风景并回到肖像后,我可以滚动到空白的第四页。这些问题大概就是您所说的“contentSize肯定存在某些问题”的意思。<br><br>此外,您的代码有很多问题。它使用一些过时的样式,例如为属性显式声明实例变量并将实例变量放在头文件中。它还患有<a href="https://twitter.com/Colin_Campbell/status/293167951132098560" rel="noreferrer noopener nofollow">Massive View Controller</a>。它确实可以用现代风格重写,并使用<code>UITabBarController</code>和<code>UIPageViewController</code>等功能。<br><br>无论如何,您可能既没有时间也没有时间去做那么多工作,所以我将向您展示如何解决<code>contentSize</code>问题并同时缩小VC的范围。<br><br>我将创建一个名为<code>UIScrollView</code>的<code>ImageScrollView</code>子类。您给了我一组图像,我将负责设置其子视图并在旋转后与页面边界对齐。这是我的头文件:<br><code>ImageScrollView.h</code><pre><code>#import &lt;UIKit/UIKit.h&gt;

@interface ImageScrollView : UIScrollView

@property (nonatomic, copy) NSArray *images;

@end
</code></pre><br>为了实现这一点,我需要一些实例变量:<br><code>ImageScrollView.m</code><pre><code>#import &#34;ImageScrollView.h&#34;
#import &lt;tgmath.h&gt;

@implementation ImageScrollView {
    NSMutableArray *imageSubviews;
    CGSize priorSize;
    CGFloat pageNumber;
    BOOL needsToSyncSubviewsWithImages : 1;
}
</code></pre><br>无论如何,首先我将实现 public API,它只是<code>images</code>属性:<br><pre><code>#pragma mark - Public API

@synthesize images = _images;

- (void)setImages:(NSArray *)images {
    _images = ;
    needsToSyncSubviewsWithImages = YES;
}
</code></pre><br>请注意,当您设置<code>images</code>数组时,我<strong>不要</strong>立即创建子视图。现在,我只设置<code>needsToSyncSubviewsWithImages</code>标志,这样我就知道在布局阶段要这样做。<br><pre><code>#pragma mark - UIView overrides
</code></pre><br>接下来,我需要重写<code>layoutSubviews</code>,以便可以在布局阶段进行实际工作。如果我的<code>layoutSubviews</code>数组已更改,或者我的<code>subviews</code>已更改,则系统会在布局阶段向我发送<code>bounds</code>。<br><br>因为我是滚动视图,并且因为滚动视图的<code>contentOffset</code>实际上只是其<code>bounds.origin</code>的别名,所以系统在每次滚动视图滚动时都会向我发送<code>layoutSubviews</code>。因此,我想小心地仅在<code>layoutSubviews</code>中执行必要的工作。<br><pre><code>- (void)layoutSubviews {
</code></pre><br>我要做的第一件事是调用<code>super</code>,它可以让自动布局工作(如果正在使用)并更新我的滚动指示器(如果它们可见)。<br><pre><code>    ;
</code></pre><br>接下来,如果有新图像,则设置显示它们的子视图。<br><pre><code>    if (needsToSyncSubviewsWithImages) {
      ;
    }
</code></pre><br>接下来,如果我设置了新的子视图,或者更改了大小,则将子视图的框架布置为新的大小,并与页面边界对齐。<br><pre><code>    if (needsToSyncSubviewsWithImages || !CGSizeEqualToSize(self.bounds.size, priorSize)) {
      ;
    }
</code></pre><br>最后,我更新我的状态。<br><pre><code>    needsToSyncSubviewsWithImages = NO;
    priorSize = self.bounds.size;
    ;
}
</code></pre><br>当然,我将所有实际工作委托给了辅助方法,因此现在我需要实现这些方法。<br><pre><code>#pragma mark - Implementation details
</code></pre><br>要将子视图与图像同步,我需要做三件事。我需要确保我实际上已经分配了<code>imageSubviews</code>数组,我需要确保每个图像都在子视图中,并且我需要确保我没有任何额外的图像子视图(以防我的<code>images</code>数组变小了) )。<br><pre><code>- (void)syncSubviewsWithImages {
    ;
    ;
    ;
}

- (void)ensureImageSubviewsArrayExists {
    if (imageSubviews == nil) {
      imageSubviews = ;
    }
}

- (void)putImagesInSubviews {
    [self.images enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop) {
      ;
    }];
}

- (void)removeExtraSubviews {
    while (imageSubviews.count &gt; self.images.count) {
      ;
      ;
    }
}

- (void)putImage:(UIImage *)image inSubviewAtIndex:(NSUInteger)i {
    UIImageView *imageView = ;
    imageView.image = image;
}
</code></pre><br>当我想获取索引的图像视图时,可能会发现实际上尚未创建足够的子视图,因此我按需创建了它们:<br><pre><code>- (UIImageView *)imageViewAtIndex:(NSUInteger)i {
    while (i &gt;= imageSubviews.count) {
      UIView *view = [ init];
      view.contentMode = UIViewContentModeScaleAspectFit;
      view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
      ;
      ;
    }
    return imageSubviews;
}
</code></pre><br>请注意,我已经设置了<code>autoresizingMask</code>,以使自动调整大小实际上不会修改我的子视图框架。相反,我将“手动”布置它们。<br><br>好的,现在我需要实现设置子视图框架并在尺寸改变时对齐页面边界的方法。<br><pre><code>- (void)layoutForNewSize {
    ;
    ;
}
</code></pre><br>设置子视图框架需要在它们上循环,从左到右布置它们。布置完最后一个后,我知道了<code>contentSize</code>。注意,我只需要循环<code>imageSubviews</code>,而不是<code>self.subviews</code>,因为<code>self.subviews</code>也包含滚动指示器。<br><pre><code>- (void)setSubviewFramesAndContentSize {
    CGRect frame = self.bounds;
    frame.origin = CGPointZero;
    for (UIView *subview in imageSubviews) {
      subview.frame = frame;
      frame.origin.x += frame.size.width;
    }
    self.contentSize = CGSizeMake(frame.origin.x, frame.size.height);
}
</code></pre><br>为了与最近的页面对齐,我根据最近的已知页面编号和新尺寸设置了<code>contentOffset</code>。<br><pre><code>- (void)alignToNearestPage {
    self.contentOffset = CGPointMake(pageNumber * self.bounds.size.width, 0);
}
</code></pre><br>最后,每次滚动时我都需要更新页码,以便轮换使用:<br><pre><code>- (void)updatePageNumber {
    // Note that self.contentOffset == self.bounds.origin.
    CGRect bounds = self.bounds;
    pageNumber = fmin(round(bounds.origin.x / bounds.size.width), self.images.count - 1);
}

@end
</code></pre><br>现在,您可以更新<code>ViewController</code>以使用<code>ImageScrollView</code>。这主要涉及撕碎东西:<br><pre><code>-(void)configureImageTab{
    //Page control
    pageControl = ;
    pageControl.currentPageIndicatorTintColor = ;
    pageControl.pageIndicatorTintColor = ;
    ;
    pageControl.translatesAutoresizingMaskIntoConstraints = NO;


    //Configuring scrollView
    self.scrollView = [ initWithFrame:CGRectMake(0, 0, self.imageSegmentView.frame.size.width, self.imageSegmentView.frame.size.height-pageControl.frame.size.height)];
    self.scrollView.backgroundColor = ;
    self.scrollView.pagingEnabled = YES;
    self.scrollView.delegate = self;
    self.scrollView.userInteractionEnabled = YES;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;

    //Adding imageURLS to array
    photos = @[ , , ];
    self.scrollView.images = photos;

    pageControl.numberOfPages = ;

    ;
    ;


    NSDictionary *views = @{@&#34;pageControl&#34; : pageControl, @&#34;scrollView&#34; : self.scrollView};

    -0-|&#34; options:0 metrics:nil views:views]];
    |&#34; options:0 metrics:nil views:views]];

    -1--1-|&#34; options:0 metrics:nil views:views]];

    ];
}
</code></pre><br>您还需要在头文件中将<code>scrollView</code>的声明类型更改为<code>ImageScrollView</code>。您可以完全消除<code>viewWillLayoutSubviews</code>,<code>willRotateToInterfaceOrientation:duration:</code>和<code>willAnimateRotationToInterfaceOrientation:duration:</code>方法。<br><br>我已经将您的测试项目的修改后的版本上传到<a href="https://github.com/mayoff/rotation-with-paging-scroll-view" rel="noreferrer noopener nofollow">this github repository</a>。</p>
                                   
                                                <p style="font-size: 20px;">关于ios - ScrollView和ImageView-多次旋转设备后图像未居中,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/25920880/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/25920880/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - ScrollView和ImageView-多次旋转设备后图像未居中