Skip to content
This repository has been archived by the owner on Feb 8, 2023. It is now read-only.

Commit

Permalink
Merge pull request #6 from teambition/feature/mini-map
Browse files Browse the repository at this point in the history
Support to mini map & Update deployment target to 9.0
  • Loading branch information
wzxha authored May 21, 2018
2 parents 51308b2 + ada0f39 commit eaf2a0a
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 9 deletions.
8 changes: 6 additions & 2 deletions PhotoBrowser.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
1E11D49920AAC4CD00E48785 /* MiniMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E11D49820AAC4CD00E48785 /* MiniMap.swift */; };
4A0739991C98FB3C0004FEA5 /* PBCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0739981C98FB3C0004FEA5 /* PBCustomView.swift */; };
4A0B3FDF1C76DB300049338C /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A0B3FDE1C76DB300049338C /* Kingfisher.framework */; };
4A52D1E41C72CB2E001C257B /* PhotoBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A52D1E31C72CB2E001C257B /* PhotoBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand All @@ -23,6 +24,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
1E11D49820AAC4CD00E48785 /* MiniMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiniMap.swift; sourceTree = "<group>"; };
4A0739981C98FB3C0004FEA5 /* PBCustomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBCustomView.swift; sourceTree = "<group>"; };
4A0B3FDE1C76DB300049338C /* Kingfisher.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kingfisher.framework; path = Carthage/Build/iOS/Kingfisher.framework; sourceTree = "<group>"; };
4A52D1E01C72CB2E001C257B /* PhotoBrowser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PhotoBrowser.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -85,6 +87,7 @@
4A0739981C98FB3C0004FEA5 /* PBCustomView.swift */,
A18117DF1E83B66900F8CE8E /* CustomPhotoBroswerManager.swift */,
C4C79EA02057808200C92C0C /* UIView+Frame.swift */,
1E11D49820AAC4CD00E48785 /* MiniMap.swift */,
4A52D1E51C72CB2E001C257B /* Info.plist */,
);
path = PhotoBrowser;
Expand Down Expand Up @@ -178,6 +181,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1E11D49920AAC4CD00E48785 /* MiniMap.swift in Sources */,
864F45E81E9F0A4900FF9215 /* Skitch.swift in Sources */,
4A52D2301C72F47C001C257B /* Photo.swift in Sources */,
4A0739991C98FB3C0004FEA5 /* PBCustomView.swift in Sources */,
Expand Down Expand Up @@ -241,7 +245,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -293,7 +297,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand Down
138 changes: 138 additions & 0 deletions PhotoBrowser/MiniMap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// MiniMap.swift
// ImageMap
//
// Created by wzxjiang on 2018/5/10.
// Copyright © 2018 wzxjiang. All rights reserved.
//

import UIKit

struct Ratios {
let top: CGFloat
let left: CGFloat
let width: CGFloat
let height: CGFloat

static let zero = Ratios(top: 0, left: 0, width: 0, height: 0)

init(top: CGFloat, left: CGFloat, width: CGFloat, height: CGFloat) {
self.top = min(max(top, 0), 1)
self.left = min(max(left, 0), 1)
self.width = min(max(width, 0), 1)
self.height = min(max(height, 0), 1)
}
}

public class MiniMap: UIView {
public var image = UIImage() {
didSet {
updateimageViewSize()
imageView.image = image
}
}

private let imageView = UIImageView()

private let backgroundLayer = CALayer()

private var lineLayer = CAShapeLayer()

private var maskLayer = CAShapeLayer()

private let _size: CGSize

var ratios: Ratios = .zero {
didSet {
updateLayer()
}
}

private var imageViewSize: CGSize

required public init(size: CGSize) {
_size = size
imageViewSize = size
super.init(frame: .zero)
setup()
addLayer()
}

required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func setup() {
backgroundColor = .black
addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
updateImageViewSize()
}

private func addLayer() {
backgroundLayer.frame = CGRect(origin: .zero, size: _size)
backgroundLayer.backgroundColor = UIColor.black.withAlphaComponent(0.5).cgColor
imageView.layer.addSublayer(backgroundLayer)

lineLayer.strokeColor = UIColor.white.cgColor
lineLayer.fillColor = UIColor.clear.cgColor
imageView.layer.addSublayer(lineLayer)
}

private func updateLayer() {
var top: CGFloat = imageViewSize.height * ratios.top
var left: CGFloat = imageViewSize.width * ratios.left
let width: CGFloat = imageViewSize.width * ratios.width
let height: CGFloat = imageViewSize.height * ratios.height

if top + height > imageViewSize.height {
top = imageViewSize.height - height
}

if left + width > imageViewSize.width {
left = imageViewSize.width - width
}

let maskBezierPath = UIBezierPath()
maskBezierPath.move(to: CGPoint(x: 0, y: 0))
maskBezierPath.addLine(to: CGPoint(x: 0, y: imageViewSize.height))
maskBezierPath.addLine(to: CGPoint(x: imageViewSize.width, y: imageViewSize.height))
maskBezierPath.addLine(to: CGPoint(x: imageViewSize.width, y: 0))
maskBezierPath.move(to: CGPoint(x: left, y: top))
maskBezierPath.addLine(to: CGPoint(x: left + width, y: top))
maskBezierPath.addLine(to: CGPoint(x: left + width, y: top + height))
maskBezierPath.addLine(to: CGPoint(x: left, y: top + height))
maskBezierPath.close()
maskLayer.path = maskBezierPath.cgPath
backgroundLayer.mask = maskLayer


let lineBezierPath = UIBezierPath()
lineBezierPath.move(to: CGPoint(x: left, y: top))
lineBezierPath.addLine(to: CGPoint(x: left + width, y: top))
lineBezierPath.addLine(to: CGPoint(x: left + width, y: top + height))
lineBezierPath.addLine(to: CGPoint(x: left, y: top + height))
lineBezierPath.close()
lineBezierPath.stroke()
lineLayer.path = lineBezierPath.cgPath
}

private func updateimageViewSize() {
let ratio = image.size.width / image.size.height

if image.size.width > image.size.height {
imageViewSize = CGSize(width: _size.width, height: _size.width / ratio)
} else {
imageViewSize = CGSize(width: _size.height * ratio, height: _size.height)
}

updateImageViewSize()
}

private func updateImageViewSize() {
imageView.widthAnchor.constraint(equalToConstant: imageViewSize.width).isActive = true
imageView.heightAnchor.constraint(equalToConstant: imageViewSize.height).isActive = true
}
}
96 changes: 91 additions & 5 deletions PhotoBrowser/PhotoPreviewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ import UIKit
import Kingfisher
import Photos

private extension UIDevice {
static let isIPhoneX = (UIScreen.main.bounds.size == DeviceSize.Portrait.iPhoneX || UIScreen.main.bounds.size == DeviceSize.Landscape.iPhoneX)
}

private struct DeviceSize {
struct Landscape {
static let iPhoneX = CGSize(width: 812, height: 375)
}

struct Portrait {
static let iPhoneX = CGSize(width: 375, height: 812)
}
}

// MARK: - PhotoPreviewControllerDelegate
protocol PhotoPreviewControllerDelegate: class {
var isFullScreenMode: Bool {get set}
Expand Down Expand Up @@ -56,7 +70,7 @@ class PhotoPreviewController: UIViewController {
lazy var imageView: UIImageView = self.makeImageView()
var waitingView: WaitingView?

weak var delegate:PhotoPreviewControllerDelegate?
weak var delegate: PhotoPreviewControllerDelegate?

fileprivate let minPanY: CGFloat = -10
fileprivate let maxMoveOfY: CGFloat = 250
Expand All @@ -81,10 +95,37 @@ class PhotoPreviewController: UIViewController {

fileprivate var scrollNewY: CGFloat = 0
fileprivate var scrollOldY: CGFloat = 0
fileprivate var isFullScreenMode: Bool = false
fileprivate var isFullScreenMode: Bool = false {
didSet {
updateMiniMapLayout()
}
}

fileprivate var panLastY: CGFloat = 0


fileprivate var miniMap: MiniMap?

fileprivate var miniMapTopConstraint: NSLayoutConstraint?

private func makeMiniMap() -> MiniMap {
let miniMap = MiniMap(size: miniMapSize)
miniMap.isHidden = true
view.addSubview(miniMap)
miniMap.translatesAutoresizingMaskIntoConstraints = false
miniMap.widthAnchor.constraint(equalToConstant: miniMapSize.width).isActive = true
miniMap.heightAnchor.constraint(equalToConstant: miniMapSize.height).isActive = true
miniMap.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15).isActive = true
if #available(iOS 11.0, *), UIDevice.isIPhoneX {
miniMapTopConstraint = miniMap.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: isFullScreenMode ? 15 : 15 + 44)
} else {
miniMapTopConstraint = miniMap.topAnchor.constraint(equalTo: view.topAnchor, constant: isFullScreenMode ? 15 : 15 + 64)
}
miniMapTopConstraint?.isActive = true
return miniMap
}

public var miniMapSize: CGSize = CGSize(width: 100, height: 100)

init(photo: Photo, index: NSInteger, skitches: [[String: Any]]? = nil, isSkitchButtonHidden: Bool = true) {
super.init(nibName: nil, bundle: nil)
self.index = index
Expand Down Expand Up @@ -183,6 +224,7 @@ class PhotoPreviewController: UIViewController {
}

fileprivate func setImageViewFrame(_ image: UIImage) {
miniMap?.image = image
imageView.width = screenWidth
imageView.height = image.size.height / image.size.width * screenWidth
imageView.center = self.view.center
Expand Down Expand Up @@ -218,6 +260,8 @@ class PhotoPreviewController: UIViewController {

singleTap.require(toFail: doubleTap)

miniMap = makeMiniMap()

if let image = photo.localOriginalPhoto() {
setImageViewFrame(image)
imageView.image = image
Expand Down Expand Up @@ -265,6 +309,10 @@ class PhotoPreviewController: UIViewController {
}
}

@objc fileprivate func hideMiniMap() {
miniMap?.isHidden = true
}

func updateConstraint() {
updateSkitchViewConstraint()
view.layoutIfNeeded()
Expand All @@ -276,6 +324,22 @@ class PhotoPreviewController: UIViewController {
}
return 2 * scrollView.minimumZoomScale
}

fileprivate func updateMiniMapLayout() {
guard miniMap != nil else {
return
}

if UIDevice.isIPhoneX {
miniMapTopConstraint?.constant = isFullScreenMode ? 15 : 15 + 44
} else {
miniMapTopConstraint?.constant = isFullScreenMode ? 15 : 15 + 64
}

UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}

extension PhotoPreviewController: SkitchViewDelegate {
Expand Down Expand Up @@ -304,6 +368,7 @@ extension PhotoPreviewController {
guard let delegate = delegate else {
return
}
isFullScreenMode = !isFullScreenMode
delegate.isFullScreenMode = !delegate.isFullScreenMode
}

Expand Down Expand Up @@ -344,11 +409,31 @@ extension PhotoPreviewController: UIScrollViewDelegate {
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideMiniMap), object: nil)

scrollNewY = scrollView.contentOffset.y
if (scrollView.contentOffset.y < minPanY || isPanning) && !isZooming {
doPan(scrollView.panGestureRecognizer)
}
scrollOldY = scrollNewY

miniMap?.isHidden = scrollView.contentSize.width <= view.frame.width || moveImage != nil

miniMap?.ratios =
Ratios(
top: scrollView.contentOffset.y / scrollView.contentSize.height,
left: scrollView.contentOffset.x / scrollView.contentSize.width,
width: view.frame.width / scrollView.contentSize.width,
height: view.frame.height / scrollView.contentSize.height
)
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView.panGestureRecognizer.state == .ended {
perform(#selector(hideMiniMap), with: self, afterDelay: 3)
} else {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideMiniMap), object: nil)
}
}
}

Expand Down Expand Up @@ -421,12 +506,12 @@ extension PhotoPreviewController {
// 判断是否向下拖拽
isDirectionDown = panCurrentY > panLastY
panLastY = panCurrentY

// 拖拽进度
let progress = (panCurrentY - panBeginY) / maxMoveOfY
panningProgress = min(progress, 1.0)
delegate?.doDraging(panningProgress)

if panCurrentY > panBeginY {
moveImage?.width = imageWidthBeforeDrag - (imageWidthBeforeDrag - imageWidthBeforeDrag * minZoom) * panningProgress
moveImage?.height = imageHeightBeforeDrag - (imageHeightBeforeDrag - imageHeightBeforeDrag * minZoom) * panningProgress
Expand Down Expand Up @@ -462,6 +547,7 @@ extension PhotoPreviewController {
self.moveImage?.removeFromSuperview()
self.moveImage = nil
self.updateSkitchButtonStatus(false)
self.miniMap?.isHidden = self.scrollView.contentSize.width <= self.view.frame.width
})
} else {
guard let image = moveImage else { return }
Expand Down
4 changes: 2 additions & 2 deletions PhotoBrowserDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -316,7 +316,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand Down

0 comments on commit eaf2a0a

Please sign in to comment.