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.

145 lines
5.1 KiB

2 years ago
// Copyright 2020 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 GooglePlaces
import UIKit
struct AttributedPhoto {
var image: UIImage
var attributions: NSAttributedString
}
/// Represents a place photo, along with the attributions which are required to be displayed along
/// with it.
private class ImageAndAttributionView: UIView {
private let margin: CGFloat = 30
private let textViewHeight: CGFloat = 50
lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 10
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
lazy var attributionView: UITextView = {
let textView = UITextView()
textView.delegate = self
textView.isScrollEnabled = false
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
init(attributedPhoto: AttributedPhoto) {
super.init(frame: .zero)
addSubview(imageView)
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: topAnchor, constant: margin),
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: margin),
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -margin),
])
addSubview(attributionView)
NSLayoutConstraint.activate([
attributionView.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: margin),
attributionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -margin),
attributionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: margin),
attributionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -margin),
attributionView.heightAnchor.constraint(equalToConstant: textViewHeight),
])
imageView.image = attributedPhoto.image
attributionView.attributedText = attributedPhoto.attributions
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension ImageAndAttributionView: UITextViewDelegate {
// Make links clickable.
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange)
-> Bool
{
return true
}
}
/// A horizontally-paging scroll view that displays a list of photo images and their attributions.
@objc(PagingPhotoView)
class PagingPhotoView: UIScrollView {
private var pageViews: [ImageAndAttributionView] = []
private var contentView = UIView()
public var shouldRedraw = false
func updatePhotos(_ photoList: [AttributedPhoto]) {
// Reset state of pageViews and contentView
pageViews = []
contentView.removeFromSuperview()
contentView = UIView()
isPagingEnabled = true
addSubview(contentView)
// Generate page views, then add them to the scroll view's content view.
for (index, photo) in photoList.enumerated() {
let pageView = ImageAndAttributionView(attributedPhoto: photo)
pageView.frame = CGRect(
x: CGFloat(index) * frame.size.width, y: 0, width: frame.size.width,
height: frame.size.height)
pageViews.append(pageView)
contentView.addSubview(pageView)
}
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.widthAnchor.constraint(
equalToConstant: bounds.width * CGFloat(pageViews.count)),
contentView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
guard shouldRedraw else { return }
shouldRedraw = false
// Update `ImageAndAttributionView` frame after rotation.
for (index, photoView) in pageViews.enumerated() {
photoView.frame = CGRect(
x: CGFloat(index) * frame.size.width, y: 0, width: frame.size.width,
height: frame.size.height)
}
// Update contentSize.
let originWidth = contentSize.width
contentSize = CGSize(
width: CGFloat(pageViews.count) * frame.size.width, height: frame.size.height)
// Re-adjust the content offset to ensure the photos are aligned properly horizontally.
if contentSize.width != 0 {
let scrollOffset = (CGFloat)(round((contentOffset.x / originWidth) * 10) / 10)
contentOffset = CGPoint(x: scrollOffset * contentSize.width, y: 0)
}
}
}