You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
6.0 KiB

2 years ago
/*
* Copyright 2016 Google LLC. All rights reserved.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#import "GooglePlacesDemos/Samples/PagingPhotoView.h"
/**
* Class to store the image and text views that display the image and attributions.
*/
@interface ImageViewAndAttribution : NSObject
@property(nonatomic, strong) UIImageView *imageView;
@property(nonatomic, strong) UITextView *attributionView;
@end
@implementation ImageViewAndAttribution
@end
@implementation AttributedPhoto
@end
@interface PagingPhotoView () <UITextViewDelegate>
@end
@implementation PagingPhotoView {
// An array of |ImageViewAndAttribution| objects representing the actual views that are
// being displayed.
NSMutableArray *_photoImageViews;
// Whether we should update the image and attribution view frames on the next |layoutSubviews|
// call. This should be set to YES whenever the frame is updated or the photos change.
BOOL _imageLayoutUpdateNeeded;
}
- (instancetype)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
_photoImageViews = [NSMutableArray array];
self.backgroundColor = [UIColor whiteColor];
self.pagingEnabled = YES;
}
return self;
}
- (void)setPhotoList:(NSArray *)photoList {
// First, remove all of the existing image and attribution subviews.
for (ImageViewAndAttribution *photoView in _photoImageViews) {
[photoView.imageView removeFromSuperview];
[photoView.attributionView removeFromSuperview];
}
[_photoImageViews removeAllObjects];
// Add the new images and attributions as subviews.
_photoList = [photoList copy];
for (AttributedPhoto *photo in photoList) {
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectZero];
textView.delegate = self;
textView.editable = NO;
textView.attributedText = photo.attributions;
[self addSubview:textView];
UIImageView *imageView = [[UIImageView alloc] initWithImage:photo.image];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.clipsToBounds = YES;
[self addSubview:imageView];
ImageViewAndAttribution *attributedView = [[ImageViewAndAttribution alloc] init];
attributedView.imageView = imageView;
attributedView.attributionView = textView;
[_photoImageViews addObject:attributedView];
}
[self updateContentSize];
_imageLayoutUpdateNeeded = YES;
}
- (void)setFrame:(CGRect)frame {
_imageLayoutUpdateNeeded = YES;
// We want to make sure that we are still scrolled to the same photo when the frame changes.
// Measure the current content offset and scroll to the same fraction along the content after the
// frame change.
CGFloat scrollOffsetFraction = 0;
if (self.contentSize.width != 0) {
scrollOffsetFraction = self.contentOffset.x / self.contentSize.width;
}
[super setFrame:frame];
[UIView performWithoutAnimation:^{
[self updateContentSize];
self.contentOffset =
CGPointMake(scrollOffsetFraction * self.contentSize.width, -self.contentInset.top);
}];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (_imageLayoutUpdateNeeded) {
[self layoutImages];
_imageLayoutUpdateNeeded = NO;
// Re-adjust the content offset to ensure the photos are aligned properly horizontally.
if (self.contentSize.width != 0) {
CGFloat scrollOffset =
(CGFloat)round((self.contentOffset.x / self.contentSize.width) * 10.0f) / 10.0f;
self.contentOffset =
CGPointMake(scrollOffset * self.contentSize.width, -self.contentInset.top);
}
}
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView
shouldInteractWithURL:(NSURL *)url
inRange:(NSRange)characterRange {
// Make links clickable.
return YES;
}
#pragma mark - Helper methods
/**
* Update the content size of the scroll view based on the number of photos and the view's width.
* This should be called whenever the frame changes or the number of photos has changed.
*/
- (void)updateContentSize {
CGRect insetBounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
CGFloat usableScrollViewHeight = insetBounds.size.height;
self.contentSize =
CGSizeMake(_photoImageViews.count * self.frame.size.width, usableScrollViewHeight);
}
/**
* Updates the frames of the images and attributions.
*/
- (void)layoutImages {
CGFloat contentWidth = 0;
CGFloat scrollViewWidth = self.bounds.size.width;
CGRect insetBounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
CGFloat usableScrollViewHeight = insetBounds.size.height;
// Lay out the images one after the other horizontally.
for (ImageViewAndAttribution *attributedImageView in _photoImageViews) {
UITextView *attributionView = attributedImageView.attributionView;
UIImageView *imageView = attributedImageView.imageView;
[attributionView sizeToFit];
CGFloat attributionHeight = attributionView.frame.size.height;
CGFloat imageHeight = usableScrollViewHeight - attributionHeight;
CGFloat safeAreaX = 0.0f;
// Take into account the safe areas of the device screen and do not use that space for the
// attribution text.
imageHeight -= self.safeAreaInsets.bottom;
safeAreaX = self.safeAreaInsets.left;
// Put the attribution view aligned to the same left edge as the photo, in the bottom left
// corner of the screen.
attributionView.frame = CGRectMake(contentWidth + safeAreaX, imageHeight,
scrollViewWidth - (2 * safeAreaX), attributionHeight);
imageView.frame = CGRectMake(contentWidth, 0, scrollViewWidth, imageHeight);
contentWidth += imageView.frame.size.width;
}
}
@end