forked from organicmaps/organicmaps-tmp
Merge pull request #5640 from igrechuhin/MAPSME-4096
[MAPSME-4096] [ios] Added photos view controller.
This commit is contained in:
commit
3d83f7c1f0
14 changed files with 892 additions and 9 deletions
14
iphone/Maps/Categories/UIView+Coordinates.swift
Normal file
14
iphone/Maps/Categories/UIView+Coordinates.swift
Normal file
|
@ -0,0 +1,14 @@
|
|||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func center(inContainerView containerView: UIView) -> CGPoint {
|
||||
guard let sv = superview else { return .zero }
|
||||
var centerPoint = center
|
||||
|
||||
if let scrollView = sv as? UIScrollView , scrollView.zoomScale != 1.0 {
|
||||
centerPoint.x += (scrollView.bounds.width - scrollView.contentSize.width) / 2.0 + scrollView.contentOffset.x
|
||||
centerPoint.y += (scrollView.bounds.height - scrollView.contentSize.height) / 2.0 + scrollView.contentOffset.y
|
||||
}
|
||||
return sv.convert(centerPoint, to: containerView)
|
||||
}
|
||||
}
|
23
iphone/Maps/Categories/UIView+Snapshot.swift
Normal file
23
iphone/Maps/Categories/UIView+Snapshot.swift
Normal file
|
@ -0,0 +1,23 @@
|
|||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
var snapshot: UIView {
|
||||
guard let contents = layer.contents else {
|
||||
return snapshotView(afterScreenUpdates: true)!
|
||||
}
|
||||
let snapshot: UIView
|
||||
if let view = self as? UIImageView {
|
||||
snapshot = UIImageView(image: view.image)
|
||||
snapshot.bounds = view.bounds
|
||||
} else {
|
||||
snapshot = UIView(frame: frame)
|
||||
snapshot.layer.contents = contents
|
||||
snapshot.layer.bounds = layer.bounds
|
||||
}
|
||||
snapshot.layer.cornerRadius = layer.cornerRadius
|
||||
snapshot.layer.masksToBounds = layer.masksToBounds
|
||||
snapshot.contentMode = contentMode
|
||||
snapshot.transform = transform
|
||||
return snapshot
|
||||
}
|
||||
}
|
|
@ -21,3 +21,8 @@ func iPhoneSpecific( _ f: () -> Void) {
|
|||
func toString(_ cls: AnyClass) -> String {
|
||||
return String(describing: cls)
|
||||
}
|
||||
|
||||
func statusBarHeight() -> CGFloat {
|
||||
let statusBarSize = UIApplication.shared.statusBarFrame.size
|
||||
return min(statusBarSize.height, statusBarSize.width)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,30 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
1D3623260D0F684500981E51 /* MapsAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* MapsAppDelegate.mm */; };
|
||||
1D60589B0D05DD56006BFB54 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.mm */; };
|
||||
3404163B1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404163A1E7BDFE000E2B6D6 /* PhotosViewController.swift */; };
|
||||
3404163C1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404163A1E7BDFE000E2B6D6 /* PhotosViewController.swift */; };
|
||||
3404163D1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404163A1E7BDFE000E2B6D6 /* PhotosViewController.swift */; };
|
||||
340416431E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416421E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift */; };
|
||||
340416441E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416421E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift */; };
|
||||
340416451E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416421E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift */; };
|
||||
340416471E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416461E7BF28E00E2B6D6 /* UIView+Snapshot.swift */; };
|
||||
340416481E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416461E7BF28E00E2B6D6 /* UIView+Snapshot.swift */; };
|
||||
340416491E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416461E7BF28E00E2B6D6 /* UIView+Snapshot.swift */; };
|
||||
3404164B1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */; };
|
||||
3404164C1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */; };
|
||||
3404164D1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */; };
|
||||
3404164F1E7C085F00E2B6D6 /* PhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164E1E7C085F00E2B6D6 /* PhotoViewController.swift */; };
|
||||
340416501E7C086000E2B6D6 /* PhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164E1E7C085F00E2B6D6 /* PhotoViewController.swift */; };
|
||||
340416511E7C086000E2B6D6 /* PhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404164E1E7C085F00E2B6D6 /* PhotoViewController.swift */; };
|
||||
340416531E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416521E7C09C200E2B6D6 /* PhotoScalingView.swift */; };
|
||||
340416541E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416521E7C09C200E2B6D6 /* PhotoScalingView.swift */; };
|
||||
340416551E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416521E7C09C200E2B6D6 /* PhotoScalingView.swift */; };
|
||||
340416571E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416561E7C0D4100E2B6D6 /* PhotosOverlayView.swift */; };
|
||||
340416581E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416561E7C0D4100E2B6D6 /* PhotosOverlayView.swift */; };
|
||||
340416591E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340416561E7C0D4100E2B6D6 /* PhotosOverlayView.swift */; };
|
||||
3404165B1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404165A1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift */; };
|
||||
3404165C1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404165A1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift */; };
|
||||
3404165D1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404165A1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift */; };
|
||||
340474F01E08199D00C92850 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340474DC1E08199D00C92850 /* Crashlytics.framework */; };
|
||||
340474F11E08199D00C92850 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340474DC1E08199D00C92850 /* Crashlytics.framework */; };
|
||||
340474F21E08199D00C92850 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340474DC1E08199D00C92850 /* Crashlytics.framework */; };
|
||||
|
@ -1450,6 +1474,14 @@
|
|||
1D3623250D0F684500981E51 /* MapsAppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = MapsAppDelegate.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
28A0AB4B0D9B1048005BE974 /* Maps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Maps_Prefix.pch; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
29B97316FDCFA39411CA2CEA /* main.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = main.mm; sourceTree = "<group>"; };
|
||||
3404163A1E7BDFE000E2B6D6 /* PhotosViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosViewController.swift; sourceTree = "<group>"; };
|
||||
340416421E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosTransitionAnimator.swift; sourceTree = "<group>"; };
|
||||
340416461E7BF28E00E2B6D6 /* UIView+Snapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Snapshot.swift"; sourceTree = "<group>"; };
|
||||
3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Coordinates.swift"; sourceTree = "<group>"; };
|
||||
3404164E1E7C085F00E2B6D6 /* PhotoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoViewController.swift; sourceTree = "<group>"; };
|
||||
340416521E7C09C200E2B6D6 /* PhotoScalingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoScalingView.swift; sourceTree = "<group>"; };
|
||||
340416561E7C0D4100E2B6D6 /* PhotosOverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosOverlayView.swift; sourceTree = "<group>"; };
|
||||
3404165A1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosInteractionAnimator.swift; sourceTree = "<group>"; };
|
||||
340474DC1E08199D00C92850 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
||||
340474DD1E08199D00C92850 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
|
||||
340474DE1E08199D00C92850 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FBSDKCoreKit.framework; sourceTree = "<group>"; };
|
||||
|
@ -2504,6 +2536,19 @@
|
|||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
340416391E7BDFB800E2B6D6 /* Photos */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
340416521E7C09C200E2B6D6 /* PhotoScalingView.swift */,
|
||||
3404165A1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift */,
|
||||
340416561E7C0D4100E2B6D6 /* PhotosOverlayView.swift */,
|
||||
340416421E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift */,
|
||||
3404163A1E7BDFE000E2B6D6 /* PhotosViewController.swift */,
|
||||
3404164E1E7C085F00E2B6D6 /* PhotoViewController.swift */,
|
||||
);
|
||||
path = Photos;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
340474DB1E08199D00C92850 /* 3party */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2761,6 +2806,8 @@
|
|||
349D1CE21E3F836900A878FD /* UIViewController+Hierarchy.swift */,
|
||||
34F7422F1E0834F400AC1FD6 /* UIViewController+Navigation.h */,
|
||||
34F742301E0834F400AC1FD6 /* UIViewController+Navigation.mm */,
|
||||
340416461E7BF28E00E2B6D6 /* UIView+Snapshot.swift */,
|
||||
3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2845,6 +2892,7 @@
|
|||
346DB81C1E5C4F6700E3123E /* Gallery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
340416391E7BDFB800E2B6D6 /* Photos */,
|
||||
346DB81D1E5C4F6700E3123E /* Cells */,
|
||||
346DB8201E5C4F6700E3123E /* GalleryItemViewController.swift */,
|
||||
346DB8211E5C4F6700E3123E /* GalleryItemViewController.xib */,
|
||||
|
@ -4869,6 +4917,7 @@
|
|||
F6E2FF081E097BA00083EBEC /* MWMSearchHistoryManager.mm in Sources */,
|
||||
F6E2FD8E1E097BA00083EBEC /* MWMNoMapsViewController.mm in Sources */,
|
||||
34D3B0411E389D05004100F9 /* MWMEditorTextTableViewCell.mm in Sources */,
|
||||
3404163B1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */,
|
||||
F6E2FE601E097BA00083EBEC /* MWMBookmarkCell.mm in Sources */,
|
||||
F6E2FEA21E097BA00083EBEC /* MWMPPView.mm in Sources */,
|
||||
F6791B131C43DEA7007A8A6E /* MWMStartButton.mm in Sources */,
|
||||
|
@ -4933,6 +4982,7 @@
|
|||
F607C18E1C047FDC00B53A87 /* MWMSegue.mm in Sources */,
|
||||
F6E2FE301E097BA00083EBEC /* MWMStreetEditorViewController.mm in Sources */,
|
||||
F6E2FE3F1E097BA00083EBEC /* MWMPlacePageEntity.mm in Sources */,
|
||||
340416471E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */,
|
||||
F6E2FE271E097BA00083EBEC /* MWMOpeningHoursSection.mm in Sources */,
|
||||
F6E2FE241E097BA00083EBEC /* MWMOpeningHoursModel.mm in Sources */,
|
||||
F6BD33811B62403B00F2CE18 /* MWMRoutePreview.mm in Sources */,
|
||||
|
@ -5000,6 +5050,7 @@
|
|||
F6E2FEC91E097BA00083EBEC /* MWMSearchFilterTransitioning.mm in Sources */,
|
||||
FA054612155C465E001F4E37 /* SelectSetVC.mm in Sources */,
|
||||
F6E2FE811E097BA00083EBEC /* MWMPlacePageOpeningHoursDayView.mm in Sources */,
|
||||
340416571E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */,
|
||||
F6E2FD6A1E097BA00083EBEC /* MWMMapDownloaderSubplaceTableViewCell.mm in Sources */,
|
||||
FAA614B8155F16950031C345 /* AddSetVC.mm in Sources */,
|
||||
F6E2FF681E097BA00083EBEC /* MWMUnitsController.mm in Sources */,
|
||||
|
@ -5037,12 +5088,14 @@
|
|||
F6E2FE9F1E097BA00083EBEC /* MWMPlacePageLayout.mm in Sources */,
|
||||
F6E2FEC31E097BA00083EBEC /* MWMSearchFilterPresentationController.mm in Sources */,
|
||||
340475731E081A4600C92850 /* MWMStorage.mm in Sources */,
|
||||
340416531E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */,
|
||||
F6E2FD851E097BA00083EBEC /* MWMBaseMapDownloaderViewController.mm in Sources */,
|
||||
34ABA6241C2D551900FE1BEC /* MWMInputValidatorFactory.mm in Sources */,
|
||||
34943BBA1E2626B200B14F84 /* WelcomePageController.swift in Sources */,
|
||||
F6E2FEE11E097BA00083EBEC /* MWMSearchManager.mm in Sources */,
|
||||
34D3AFE11E376F7E004100F9 /* UITableView+Updates.swift in Sources */,
|
||||
F6E2FE211E097BA00083EBEC /* MWMOpeningHoursEditorViewController.mm in Sources */,
|
||||
3404164B1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */,
|
||||
349D1ADA1E2E325C004A2006 /* MWMBottomMenuView.mm in Sources */,
|
||||
F6E2FD911E097BA00083EBEC /* MWMBookmarkColorViewController.mm in Sources */,
|
||||
F6F7787A1DABC6D800B603E7 /* MWMTaxiCollectionLayout.mm in Sources */,
|
||||
|
@ -5051,6 +5104,7 @@
|
|||
34D3B0291E389D05004100F9 /* MWMEditorAdditionalNameTableViewCell.mm in Sources */,
|
||||
34D4FA621E26572D003F53EF /* FirstLaunchController.swift in Sources */,
|
||||
34C9BD041C6DB693000DC38D /* MWMViewController.mm in Sources */,
|
||||
3404165B1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */,
|
||||
340475701E081A4600C92850 /* MWMSettings.mm in Sources */,
|
||||
3404756D1E081A4600C92850 /* MWMSearch.mm in Sources */,
|
||||
3486B5071E27A4B50069C126 /* LocalNotificationManager.mm in Sources */,
|
||||
|
@ -5066,6 +5120,7 @@
|
|||
F6E2FF021E097BA00083EBEC /* MWMSearchHistoryClearCell.mm in Sources */,
|
||||
F63774EA1B59376F00BCF54D /* MWMRoutingDisclaimerAlert.mm in Sources */,
|
||||
340475081E08199E00C92850 /* MWMMyTarget.mm in Sources */,
|
||||
3404164F1E7C085F00E2B6D6 /* PhotoViewController.swift in Sources */,
|
||||
F64F19A31AB81A00006EAF7E /* MWMDownloadTransitMapAlert.mm in Sources */,
|
||||
3404754C1E081A4600C92850 /* MWMKeyboard.mm in Sources */,
|
||||
F6BD33841B6240F200F2CE18 /* MWMNavigationView.mm in Sources */,
|
||||
|
@ -5083,6 +5138,7 @@
|
|||
ED48BBB517C267F5003E7E92 /* ColorPickerView.mm in Sources */,
|
||||
34F5E0D71E3F334700B1C415 /* Types.swift in Sources */,
|
||||
F6E2FF561E097BA00083EBEC /* MWMMobileInternetViewController.mm in Sources */,
|
||||
340416431E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */,
|
||||
ED48BBBA17C2B1E2003E7E92 /* CircleView.mm in Sources */,
|
||||
F6E2FEEA1E097BA00083EBEC /* MWMSearchTextField.mm in Sources */,
|
||||
F6664C121E645A4100E703C2 /* MWMPPReviewCell.mm in Sources */,
|
||||
|
@ -5141,6 +5197,7 @@
|
|||
F6E2FF091E097BA00083EBEC /* MWMSearchHistoryManager.mm in Sources */,
|
||||
F6E2FD8F1E097BA00083EBEC /* MWMNoMapsViewController.mm in Sources */,
|
||||
34D3B0421E389D05004100F9 /* MWMEditorTextTableViewCell.mm in Sources */,
|
||||
3404163C1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */,
|
||||
F6E2FE611E097BA00083EBEC /* MWMBookmarkCell.mm in Sources */,
|
||||
F6E2FEA31E097BA00083EBEC /* MWMPPView.mm in Sources */,
|
||||
3454D7D11E07F045004AF2AD /* UIImage+RGBAData.mm in Sources */,
|
||||
|
@ -5205,6 +5262,7 @@
|
|||
F6E2FE401E097BA00083EBEC /* MWMPlacePageEntity.mm in Sources */,
|
||||
F6E2FE281E097BA00083EBEC /* MWMOpeningHoursSection.mm in Sources */,
|
||||
3406FA161C6E0C3300E9FAD2 /* MWMMapDownloadDialog.mm in Sources */,
|
||||
340416481E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */,
|
||||
F6E2FE251E097BA00083EBEC /* MWMOpeningHoursModel.mm in Sources */,
|
||||
34C9BD031C6DB693000DC38D /* MWMTableViewController.mm in Sources */,
|
||||
F6E2FD8C1E097BA00083EBEC /* MWMNoMapsView.mm in Sources */,
|
||||
|
@ -5272,6 +5330,7 @@
|
|||
F682249B1E5B104600BC1C18 /* PPHotelDescriptionCell.swift in Sources */,
|
||||
F6E2FECA1E097BA00083EBEC /* MWMSearchFilterTransitioning.mm in Sources */,
|
||||
3454D7DD1E07F045004AF2AD /* UISwitch+RuntimeAttributes.m in Sources */,
|
||||
340416581E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */,
|
||||
F6E2FE821E097BA00083EBEC /* MWMPlacePageOpeningHoursDayView.mm in Sources */,
|
||||
F6E2FD6B1E097BA00083EBEC /* MWMMapDownloaderSubplaceTableViewCell.mm in Sources */,
|
||||
6741AA021BF340DE002C974C /* BookmarksRootVC.mm in Sources */,
|
||||
|
@ -5309,12 +5368,14 @@
|
|||
F6E2FF301E097BA00083EBEC /* MWMSearchCommonCell.mm in Sources */,
|
||||
F6E2FEA01E097BA00083EBEC /* MWMPlacePageLayout.mm in Sources */,
|
||||
F6E2FEC41E097BA00083EBEC /* MWMSearchFilterPresentationController.mm in Sources */,
|
||||
340416541E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */,
|
||||
34ABA6251C2D551900FE1BEC /* MWMInputValidatorFactory.mm in Sources */,
|
||||
F6E2FD861E097BA00083EBEC /* MWMBaseMapDownloaderViewController.mm in Sources */,
|
||||
F6E2FEE21E097BA00083EBEC /* MWMSearchManager.mm in Sources */,
|
||||
F6E2FE221E097BA00083EBEC /* MWMOpeningHoursEditorViewController.mm in Sources */,
|
||||
34943BBB1E2626B200B14F84 /* WelcomePageController.swift in Sources */,
|
||||
34D3AFE21E376F7E004100F9 /* UITableView+Updates.swift in Sources */,
|
||||
3404164C1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */,
|
||||
6741AA141BF340DE002C974C /* MWMMultilineLabel.mm in Sources */,
|
||||
349D1ADB1E2E325C004A2006 /* MWMBottomMenuView.mm in Sources */,
|
||||
F6E2FD921E097BA00083EBEC /* MWMBookmarkColorViewController.mm in Sources */,
|
||||
|
@ -5323,6 +5384,7 @@
|
|||
3454D7CB1E07F045004AF2AD /* UIColor+MapsMeColor.mm in Sources */,
|
||||
34D3B02A1E389D05004100F9 /* MWMEditorAdditionalNameTableViewCell.mm in Sources */,
|
||||
340475711E081A4600C92850 /* MWMSettings.mm in Sources */,
|
||||
3404165C1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */,
|
||||
34D4FA631E26572D003F53EF /* FirstLaunchController.swift in Sources */,
|
||||
3404756E1E081A4600C92850 /* MWMSearch.mm in Sources */,
|
||||
6741AA191BF340DE002C974C /* MWMDownloaderDialogCell.mm in Sources */,
|
||||
|
@ -5338,6 +5400,7 @@
|
|||
6741AA221BF340DE002C974C /* MWMNavigationView.mm in Sources */,
|
||||
F6E2FF031E097BA00083EBEC /* MWMSearchHistoryClearCell.mm in Sources */,
|
||||
340475091E08199E00C92850 /* MWMMyTarget.mm in Sources */,
|
||||
340416501E7C086000E2B6D6 /* PhotoViewController.swift in Sources */,
|
||||
674A7E301C0DB10B003D48E1 /* MWMMapWidgets.mm in Sources */,
|
||||
3404754D1E081A4600C92850 /* MWMKeyboard.mm in Sources */,
|
||||
34EF94291C05A6F30050B714 /* MWMSegue.mm in Sources */,
|
||||
|
@ -5355,6 +5418,7 @@
|
|||
6741AA281BF340DE002C974C /* MWMAlert.mm in Sources */,
|
||||
F6E2FF571E097BA00083EBEC /* MWMMobileInternetViewController.mm in Sources */,
|
||||
34F5E0D81E3F334700B1C415 /* Types.swift in Sources */,
|
||||
340416441E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */,
|
||||
6741AA291BF340DE002C974C /* ColorPickerView.mm in Sources */,
|
||||
6741AA2B1BF340DE002C974C /* CircleView.mm in Sources */,
|
||||
F6E2FEEB1E097BA00083EBEC /* MWMSearchTextField.mm in Sources */,
|
||||
|
@ -5413,6 +5477,7 @@
|
|||
F6E2FF0A1E097BA00083EBEC /* MWMSearchHistoryManager.mm in Sources */,
|
||||
F6E2FD901E097BA00083EBEC /* MWMNoMapsViewController.mm in Sources */,
|
||||
34D3B0431E389D05004100F9 /* MWMEditorTextTableViewCell.mm in Sources */,
|
||||
3404163D1E7BDFE000E2B6D6 /* PhotosViewController.swift in Sources */,
|
||||
F6E2FE621E097BA00083EBEC /* MWMBookmarkCell.mm in Sources */,
|
||||
F6E2FEA41E097BA00083EBEC /* MWMPPView.mm in Sources */,
|
||||
849CF69F1DE842290024A8A5 /* MWMStartButton.mm in Sources */,
|
||||
|
@ -5477,6 +5542,7 @@
|
|||
3404756C1E081A4600C92850 /* MWMSearch+CoreSpotlight.mm in Sources */,
|
||||
F6E2FE321E097BA00083EBEC /* MWMStreetEditorViewController.mm in Sources */,
|
||||
F6E2FE411E097BA00083EBEC /* MWMPlacePageEntity.mm in Sources */,
|
||||
340416491E7BF28E00E2B6D6 /* UIView+Snapshot.swift in Sources */,
|
||||
F6E2FE291E097BA00083EBEC /* MWMOpeningHoursSection.mm in Sources */,
|
||||
849CF6D31DE842290024A8A5 /* MWMMapDownloadDialog.mm in Sources */,
|
||||
F6E2FE261E097BA00083EBEC /* MWMOpeningHoursModel.mm in Sources */,
|
||||
|
@ -5544,6 +5610,7 @@
|
|||
F6E2FECB1E097BA00083EBEC /* MWMSearchFilterTransitioning.mm in Sources */,
|
||||
849CF70D1DE842290024A8A5 /* MWMAddPlaceNavigationBar.mm in Sources */,
|
||||
F6E2FE831E097BA00083EBEC /* MWMPlacePageOpeningHoursDayView.mm in Sources */,
|
||||
340416591E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */,
|
||||
F6E2FD6C1E097BA00083EBEC /* MWMMapDownloaderSubplaceTableViewCell.mm in Sources */,
|
||||
849CF70F1DE842290024A8A5 /* MWMNavigationInfoView.mm in Sources */,
|
||||
F6E2FF6A1E097BA00083EBEC /* MWMUnitsController.mm in Sources */,
|
||||
|
@ -5581,12 +5648,14 @@
|
|||
F6E2FEA11E097BA00083EBEC /* MWMPlacePageLayout.mm in Sources */,
|
||||
F6E2FEC51E097BA00083EBEC /* MWMSearchFilterPresentationController.mm in Sources */,
|
||||
F6E2FD871E097BA00083EBEC /* MWMBaseMapDownloaderViewController.mm in Sources */,
|
||||
340416551E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */,
|
||||
F6E2FEE31E097BA00083EBEC /* MWMSearchManager.mm in Sources */,
|
||||
34943BBC1E2626B200B14F84 /* WelcomePageController.swift in Sources */,
|
||||
F6E2FE231E097BA00083EBEC /* MWMOpeningHoursEditorViewController.mm in Sources */,
|
||||
34D3AFE31E376F7E004100F9 /* UITableView+Updates.swift in Sources */,
|
||||
849CF72F1DE842290024A8A5 /* MWMInputValidator.mm in Sources */,
|
||||
349D1ADC1E2E325C004A2006 /* MWMBottomMenuView.mm in Sources */,
|
||||
3404164D1E7BF42E00E2B6D6 /* UIView+Coordinates.swift in Sources */,
|
||||
F6E2FD931E097BA00083EBEC /* MWMBookmarkColorViewController.mm in Sources */,
|
||||
849CF7331DE842290024A8A5 /* MWMInputValidatorFactory.mm in Sources */,
|
||||
F6E2FDA51E097BA00083EBEC /* MWMCuisineEditorViewController.mm in Sources */,
|
||||
|
@ -5595,6 +5664,7 @@
|
|||
34D3B02B1E389D05004100F9 /* MWMEditorAdditionalNameTableViewCell.mm in Sources */,
|
||||
849CF7371DE842290024A8A5 /* MWMTaxiCollectionLayout.mm in Sources */,
|
||||
3454D7C61E07F045004AF2AD /* UIButton+Orientation.mm in Sources */,
|
||||
3404165D1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */,
|
||||
340475571E081A4600C92850 /* Statistics.mm in Sources */,
|
||||
3486B5091E27A4B50069C126 /* LocalNotificationManager.mm in Sources */,
|
||||
F6E2FEFE1E097BA00083EBEC /* MWMSearchCategoryCell.mm in Sources */,
|
||||
|
@ -5610,6 +5680,7 @@
|
|||
849CF74C1DE842290024A8A5 /* MWMLocationNotFoundAlert.mm in Sources */,
|
||||
F6E2FF041E097BA00083EBEC /* MWMSearchHistoryClearCell.mm in Sources */,
|
||||
340475541E081A4600C92850 /* MWMCustomFacebookEvents.mm in Sources */,
|
||||
340416511E7C086000E2B6D6 /* PhotoViewController.swift in Sources */,
|
||||
3404750A1E08199E00C92850 /* MWMMyTarget.mm in Sources */,
|
||||
849CF7561DE842290024A8A5 /* MWMRoutingDisclaimerAlert.mm in Sources */,
|
||||
849CF7581DE842290024A8A5 /* MWMDownloadTransitMapAlert.mm in Sources */,
|
||||
|
@ -5627,6 +5698,7 @@
|
|||
849CF7631DE842290024A8A5 /* MWMAlert.mm in Sources */,
|
||||
34F5E0D91E3F334700B1C415 /* Types.swift in Sources */,
|
||||
F6E2FF581E097BA00083EBEC /* MWMMobileInternetViewController.mm in Sources */,
|
||||
340416451E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */,
|
||||
849CF7651DE842290024A8A5 /* ColorPickerView.mm in Sources */,
|
||||
F6E2FEEC1E097BA00083EBEC /* MWMSearchTextField.mm in Sources */,
|
||||
F6664C141E645A4100E703C2 /* MWMPPReviewCell.mm in Sources */,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
typedef UIView * (^MWMPlacePageButtonsDismissBlock)(NSInteger);
|
||||
|
||||
@protocol MWMPlacePageButtonsProtocol<NSObject>
|
||||
|
||||
- (void)editPlace;
|
||||
|
@ -8,7 +10,9 @@
|
|||
- (void)taxiTo;
|
||||
- (void)showAllReviews;
|
||||
- (void)showAllFacilities;
|
||||
- (void)showPhotoAtIndex:(NSUInteger)index;
|
||||
- (void)showPhotoAtIndex:(NSInteger)index
|
||||
referenceView:(UIView *)referenceView
|
||||
referenceViewWhenDismissingHandler:(MWMPlacePageButtonsDismissBlock)referenceViewWhenDismissingHandler;
|
||||
- (void)showGalery;
|
||||
|
||||
@end
|
||||
|
|
|
@ -311,19 +311,28 @@ void logSponsoredEvent(MWMPlacePageData * data, NSString * eventName)
|
|||
[[MapViewController controller] openUrl:self.data.URLToAllReviews];
|
||||
}
|
||||
|
||||
- (void)showPhotoAtIndex:(NSUInteger)index
|
||||
- (void)showPhotoAtIndex:(NSInteger)index
|
||||
referenceView:(UIView *)referenceView
|
||||
referenceViewWhenDismissingHandler:(MWMPlacePageButtonsDismissBlock)referenceViewWhenDismissingHandler
|
||||
{
|
||||
logSponsoredEvent(self.data, kPlacePageHotelGallery);
|
||||
auto model = self.data.photos[index];
|
||||
auto galleryVc = [MWMGalleryItemViewController instanceWithModel:model];
|
||||
[[MapViewController controller].navigationController pushViewController:galleryVc animated:YES];
|
||||
auto galleryModel = [[MWMGalleryModel alloc] initWithTitle:self.hotelName items:self.data.photos];
|
||||
auto initialPhoto = galleryModel.items[index];
|
||||
auto photoVC = [[MWMPhotosViewController alloc] initWithPhotos:galleryModel
|
||||
initialPhoto:initialPhoto
|
||||
referenceView:referenceView];
|
||||
photoVC.referenceViewForPhotoWhenDismissingHandler = ^UIView *(MWMGalleryItemModel * photo) {
|
||||
return referenceViewWhenDismissingHandler([galleryModel.items indexOfObject:photo]);
|
||||
};
|
||||
|
||||
[[MapViewController controller] presentViewController:photoVC animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showGalery
|
||||
{
|
||||
logSponsoredEvent(self.data, kPlacePageHotelGallery);
|
||||
auto galleryVc = [MWMGalleryViewController instanceWithModel:[[MWMGalleryModel alloc]
|
||||
initWithTitle:self.hotelName items:self.data.photos]];
|
||||
auto galleryModel = [[MWMGalleryModel alloc] initWithTitle:self.hotelName items:self.data.photos];
|
||||
auto galleryVc = [MWMGalleryViewController instanceWithModel:galleryModel];
|
||||
[[MapViewController controller].navigationController pushViewController:galleryVc animated:YES];
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,12 @@ extension PPHotelCarouselCell: UICollectionViewDelegate, UICollectionViewDataSou
|
|||
if isLastCell(indexPath) {
|
||||
d.showGalery()
|
||||
} else {
|
||||
d.showPhoto(at: UInt(indexPath.row))
|
||||
let section = indexPath.section
|
||||
d.showPhoto(at: indexPath.item,
|
||||
referenceView: collectionView.cellForItem(at: indexPath), referenceViewWhenDismissingHandler: { index -> UIView? in
|
||||
let indexPath = IndexPath(item: index, section: section)
|
||||
return collectionView.cellForItem(at: indexPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,17 @@ final class GalleryViewController: MWMCollectionViewController {
|
|||
|
||||
// MARK: UICollectionViewDelegate
|
||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
show(GalleryItemViewController.instance(model: model.items[indexPath.item]), sender: nil)
|
||||
let currentPhoto = model.items[indexPath.item]
|
||||
let cell = collectionView.cellForItem(at: indexPath)
|
||||
let photoVC = PhotosViewController(photos: model, initialPhoto: currentPhoto, referenceView: cell)
|
||||
|
||||
photoVC.referenceViewForPhotoWhenDismissingHandler = { [weak self] photo in
|
||||
if let index = self?.model.items.index(where: {$0 === photo}) {
|
||||
let indexPath = IndexPath(item: index, section: 0)
|
||||
return collectionView.cellForItem(at: indexPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
present(photoVC, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import AlamofireImage
|
||||
import UIKit
|
||||
|
||||
final class PhotoScalingView: UIScrollView {
|
||||
private(set) lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView(frame: self.bounds)
|
||||
self.addSubview(imageView)
|
||||
return imageView
|
||||
}()
|
||||
|
||||
var photo: GalleryItemModel? {
|
||||
didSet {
|
||||
updateImage(photo)
|
||||
}
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
get { return super.frame }
|
||||
set {
|
||||
super.frame = newValue
|
||||
updateZoomScale()
|
||||
centerScrollViewContents()
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupImageScrollView()
|
||||
updateZoomScale()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
setupImageScrollView()
|
||||
updateZoomScale()
|
||||
}
|
||||
|
||||
override func didAddSubview(_ subview: UIView) {
|
||||
super.didAddSubview(subview)
|
||||
centerScrollViewContents()
|
||||
}
|
||||
|
||||
private func setupImageScrollView() {
|
||||
showsVerticalScrollIndicator = false
|
||||
showsHorizontalScrollIndicator = false
|
||||
bouncesZoom = true
|
||||
decelerationRate = UIScrollViewDecelerationRateFast
|
||||
}
|
||||
|
||||
private func centerScrollViewContents() {
|
||||
let horizontalInset: CGFloat = contentSize.width < bounds.width ? (bounds.width - contentSize.width) * 0.5 : 0
|
||||
let verticalInset: CGFloat = contentSize.height < bounds.height ? (bounds.height - contentSize.height) * 0.5 : 0
|
||||
contentInset = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset)
|
||||
}
|
||||
|
||||
private func updateImage(_ photo: GalleryItemModel?) {
|
||||
guard let photo = photo else { return }
|
||||
imageView.transform = CGAffineTransform.identity
|
||||
imageView.af_setImage(withURL: photo.imageURL,
|
||||
imageTransition: .crossDissolve(kDefaultAnimationDuration),
|
||||
completion: { [weak self] response in
|
||||
guard let s = self else { return }
|
||||
s.contentSize = response.value?.size ?? CGSize.zero
|
||||
s.imageView.frame = CGRect(origin: CGPoint.zero, size: s.contentSize)
|
||||
s.updateZoomScale()
|
||||
s.centerScrollViewContents()
|
||||
})
|
||||
}
|
||||
|
||||
private func updateZoomScale() {
|
||||
guard let image = imageView.image else { return }
|
||||
let selfSize = bounds.size
|
||||
let imageSize = image.size
|
||||
let wScale = selfSize.width / imageSize.width
|
||||
let hScale = selfSize.height / imageSize.height
|
||||
minimumZoomScale = min(wScale, hScale)
|
||||
maximumZoomScale = max(max(wScale, hScale), maximumZoomScale)
|
||||
zoomScale = minimumZoomScale
|
||||
panGestureRecognizer.isEnabled = false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import UIKit
|
||||
|
||||
final class PhotoViewController: UIViewController {
|
||||
let scalingView = PhotoScalingView()
|
||||
|
||||
let photo: GalleryItemModel
|
||||
|
||||
private(set) lazy var doubleTapGestureRecognizer: UITapGestureRecognizer = {
|
||||
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTapWithGestureRecognizer(_:)))
|
||||
gesture.numberOfTapsRequired = 2
|
||||
return gesture
|
||||
}()
|
||||
|
||||
init(photo: GalleryItemModel) {
|
||||
self.photo = photo
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
scalingView.delegate = self
|
||||
scalingView.frame = view.bounds
|
||||
scalingView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
view.addSubview(scalingView)
|
||||
|
||||
view.addGestureRecognizer(doubleTapGestureRecognizer)
|
||||
|
||||
scalingView.photo = photo
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
scalingView.frame = view.bounds
|
||||
}
|
||||
|
||||
@objc
|
||||
private func handleDoubleTapWithGestureRecognizer(_ recognizer: UITapGestureRecognizer) {
|
||||
let pointInView = recognizer.location(in: scalingView.imageView)
|
||||
var newZoomScale = scalingView.maximumZoomScale
|
||||
|
||||
if scalingView.zoomScale >= scalingView.maximumZoomScale ||
|
||||
abs(scalingView.zoomScale - scalingView.maximumZoomScale) <= 0.01 {
|
||||
newZoomScale = scalingView.minimumZoomScale
|
||||
}
|
||||
|
||||
let scrollViewSize = scalingView.bounds.size
|
||||
let width = scrollViewSize.width / newZoomScale
|
||||
let height = scrollViewSize.height / newZoomScale
|
||||
let originX = pointInView.x - (width / 2.0)
|
||||
let originY = pointInView.y - (height / 2.0)
|
||||
|
||||
let rectToZoom = CGRect(x: originX, y: originY, width: width, height: height)
|
||||
scalingView.zoom(to: rectToZoom, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotoViewController: UIScrollViewDelegate {
|
||||
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||
return scalingView.imageView
|
||||
}
|
||||
|
||||
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
|
||||
scrollView.panGestureRecognizer.isEnabled = true
|
||||
}
|
||||
|
||||
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
|
||||
if (scrollView.zoomScale == scrollView.minimumZoomScale) {
|
||||
scrollView.panGestureRecognizer.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
import UIKit
|
||||
|
||||
final class PhotosInteractionAnimator: NSObject {
|
||||
private enum Settings {
|
||||
static let returnToCenterVelocityAnimationRatio: CGFloat = 0.00007
|
||||
static let panDismissDistanceRatio: CGFloat = 0.075
|
||||
static let panDismissMaximumDuration: TimeInterval = 0.45
|
||||
}
|
||||
|
||||
var animator: UIViewControllerAnimatedTransitioning?
|
||||
var viewToHideWhenBeginningTransition: UIView?
|
||||
var shouldAnimateUsingAnimator = false
|
||||
|
||||
fileprivate var transitionContext: UIViewControllerContextTransitioning?
|
||||
|
||||
func handlePanWithPanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer, viewToPan: UIView, anchorPoint: CGPoint) {
|
||||
guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else {
|
||||
return
|
||||
}
|
||||
let translatedPanGesturePoint = gestureRecognizer.translation(in: fromView)
|
||||
let newCenterPoint = CGPoint(x: anchorPoint.x, y: anchorPoint.y + translatedPanGesturePoint.y)
|
||||
|
||||
viewToPan.center = newCenterPoint
|
||||
|
||||
let verticalDelta = newCenterPoint.y - anchorPoint.y
|
||||
let backgroundAlpha = backgroundAlphaForPanningWithVerticalDelta(verticalDelta)
|
||||
fromView.backgroundColor = fromView.backgroundColor?.withAlphaComponent(backgroundAlpha)
|
||||
|
||||
if gestureRecognizer.state == .ended, let transitionContext = transitionContext, let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) {
|
||||
let velocityY = gestureRecognizer.velocity(in: gestureRecognizer.view).y
|
||||
finishPanWith(transitionContext: transitionContext, velocityY: velocityY, verticalDelta: verticalDelta, viewToPan: viewToPan, fromView: fromView, anchorPoint: anchorPoint)
|
||||
}
|
||||
}
|
||||
|
||||
private func finishPanWith(transitionContext: UIViewControllerContextTransitioning, velocityY: CGFloat, verticalDelta: CGFloat, viewToPan: UIView, fromView: UIView, anchorPoint: CGPoint) {
|
||||
let dismissDistance = Settings.panDismissDistanceRatio * fromView.bounds.height
|
||||
let isDismissing = abs(verticalDelta) > dismissDistance
|
||||
|
||||
if isDismissing, shouldAnimateUsingAnimator, let animator = animator {
|
||||
animator.animateTransition(using: transitionContext)
|
||||
self.transitionContext = nil
|
||||
return
|
||||
}
|
||||
|
||||
let finalPageViewCenterPoint: CGPoint
|
||||
let animationDuration: TimeInterval
|
||||
let finalBackgroundAlpha: CGFloat
|
||||
|
||||
if isDismissing {
|
||||
let modifier: CGFloat = verticalDelta.sign == .plus ? 1 : -1
|
||||
let finalCenterY = fromView.bounds.midY + modifier * fromView.bounds.height
|
||||
finalPageViewCenterPoint = CGPoint(x: fromView.center.x, y: finalCenterY)
|
||||
|
||||
let duration = TimeInterval(abs(finalPageViewCenterPoint.y - viewToPan.center.y) / abs(velocityY))
|
||||
animationDuration = min(duration, Settings.panDismissMaximumDuration)
|
||||
finalBackgroundAlpha = 0.0
|
||||
} else {
|
||||
finalPageViewCenterPoint = anchorPoint
|
||||
animationDuration = TimeInterval(abs(velocityY) * Settings.returnToCenterVelocityAnimationRatio) + kDefaultAnimationDuration
|
||||
finalBackgroundAlpha = 1.0
|
||||
}
|
||||
let finalBackgroundColor = fromView.backgroundColor?.withAlphaComponent(finalBackgroundAlpha)
|
||||
finishPanWithoutAnimator(duration: animationDuration,
|
||||
viewToPan: viewToPan, fromView: fromView,
|
||||
finalPageViewCenterPoint: finalPageViewCenterPoint, finalBackgroundColor: finalBackgroundColor,
|
||||
isDismissing: isDismissing)
|
||||
}
|
||||
|
||||
private func finishPanWithoutAnimator(duration: TimeInterval, viewToPan: UIView, fromView: UIView, finalPageViewCenterPoint: CGPoint, finalBackgroundColor: UIColor?, isDismissing: Bool) {
|
||||
UIView.animate(withDuration: duration,
|
||||
delay: 0,
|
||||
options: .curveEaseOut,
|
||||
animations: {
|
||||
viewToPan.center = finalPageViewCenterPoint
|
||||
fromView.backgroundColor = finalBackgroundColor
|
||||
},
|
||||
completion: { [weak self] _ in
|
||||
guard let s = self else { return }
|
||||
if isDismissing {
|
||||
s.transitionContext?.finishInteractiveTransition()
|
||||
} else {
|
||||
s.transitionContext?.cancelInteractiveTransition()
|
||||
if !s.isRadar20070670Fixed() {
|
||||
s.fixCancellationStatusBarAppearanceBug()
|
||||
}
|
||||
}
|
||||
s.viewToHideWhenBeginningTransition?.alpha = 1.0
|
||||
s.transitionContext?.completeTransition(isDismissing && !(s.transitionContext?.transitionWasCancelled ?? false))
|
||||
s.transitionContext = nil
|
||||
})
|
||||
}
|
||||
|
||||
private func fixCancellationStatusBarAppearanceBug() {
|
||||
guard let toViewController = self.transitionContext?.viewController(forKey: UITransitionContextViewControllerKey.to),
|
||||
let fromViewController = self.transitionContext?.viewController(forKey: UITransitionContextViewControllerKey.from) else {
|
||||
return
|
||||
}
|
||||
|
||||
let statusBarViewControllerSelector = Selector("_setPresentedSta" + "tusBarViewController:")
|
||||
if toViewController.responds(to: statusBarViewControllerSelector) && fromViewController.modalPresentationCapturesStatusBarAppearance {
|
||||
toViewController.perform(statusBarViewControllerSelector, with: fromViewController)
|
||||
}
|
||||
}
|
||||
|
||||
private func isRadar20070670Fixed() -> Bool {
|
||||
return ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion.init(majorVersion: 8, minorVersion: 3, patchVersion: 0))
|
||||
}
|
||||
|
||||
private func backgroundAlphaForPanningWithVerticalDelta(_ delta: CGFloat) -> CGFloat {
|
||||
guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
let startingAlpha: CGFloat = 1.0
|
||||
let finalAlpha: CGFloat = 0.1
|
||||
let totalAvailableAlpha = startingAlpha - finalAlpha
|
||||
|
||||
let maximumDelta = CGFloat(fromView.bounds.height / 2.0)
|
||||
let deltaAsPercentageOfMaximum = min(abs(delta) / maximumDelta, 1.0)
|
||||
return startingAlpha - (deltaAsPercentageOfMaximum * totalAvailableAlpha)
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotosInteractionAnimator: UIViewControllerInteractiveTransitioning {
|
||||
func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
viewToHideWhenBeginningTransition?.alpha = 0.0
|
||||
self.transitionContext = transitionContext
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import UIKit
|
||||
|
||||
final class PhotosOverlayView: UIView {
|
||||
private var navigationBar: UINavigationBar!
|
||||
private var navigationItem: UINavigationItem!
|
||||
|
||||
weak var photosViewController: PhotosViewController?
|
||||
|
||||
var photo: GalleryItemModel? {
|
||||
didSet {
|
||||
guard let photo = photo else {
|
||||
navigationItem.title = nil
|
||||
return
|
||||
}
|
||||
guard let photosViewController = photosViewController else { return }
|
||||
if let index = photosViewController.photos.items.index(where: { $0 === photo }) {
|
||||
navigationItem.title = "\(index + 1) / \(photosViewController.photos.items.count)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupNavigationBar()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc
|
||||
private func closeButtonTapped(_ sender: UIBarButtonItem) {
|
||||
photosViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let hitView = super.hitTest(point, with: event) , hitView != self {
|
||||
return hitView
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func setupNavigationBar() {
|
||||
navigationBar = UINavigationBar()
|
||||
navigationBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
navigationBar.backgroundColor = UIColor.clear
|
||||
navigationBar.barTintColor = nil
|
||||
navigationBar.isTranslucent = true
|
||||
navigationBar.shadowImage = UIImage()
|
||||
navigationBar.setBackgroundImage(UIImage(), for: .default)
|
||||
|
||||
navigationItem = UINavigationItem(title: "")
|
||||
navigationBar.items = [navigationItem]
|
||||
addSubview(navigationBar)
|
||||
|
||||
let topConstraint = NSLayoutConstraint(item: navigationBar, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: statusBarHeight())
|
||||
let widthConstraint = NSLayoutConstraint(item: navigationBar, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0.0)
|
||||
let horizontalPositionConstraint = NSLayoutConstraint(item: navigationBar, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0.0)
|
||||
addConstraints([topConstraint,widthConstraint,horizontalPositionConstraint])
|
||||
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_nav_bar_back"), style: .plain, target: self, action: #selector(closeButtonTapped(_:)))
|
||||
}
|
||||
|
||||
func setHidden(_ hidden: Bool, animated: Bool, animation: @escaping (() -> Void)) {
|
||||
guard isHidden != hidden else { return }
|
||||
guard animated else {
|
||||
isHidden = hidden
|
||||
animation()
|
||||
return
|
||||
}
|
||||
isHidden = false
|
||||
alpha = hidden ? 1.0 : 0.0
|
||||
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration,
|
||||
delay: 0.0,
|
||||
options: [.allowAnimatedContent, .allowUserInteraction],
|
||||
animations: { [weak self] in
|
||||
self?.alpha = hidden ? 0.0 : 1.0
|
||||
animation()
|
||||
},
|
||||
completion: { [weak self] _ in
|
||||
self?.alpha = 1.0
|
||||
self?.isHidden = hidden
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
import UIKit
|
||||
|
||||
final class PhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
private enum Settings {
|
||||
static let animationDurationWithZooming = 2 * kDefaultAnimationDuration
|
||||
static let animationDurationWithoutZooming = kDefaultAnimationDuration
|
||||
static let animationDurationEndingViewFadeInRatio = 0.1
|
||||
static let animationDurationStartingViewFadeOutRatio = 0.05
|
||||
static let zoomingAnimationSpringDamping: CGFloat = 0.9
|
||||
static let animationDurationFadeRatio = 4.0 / 9.0
|
||||
}
|
||||
|
||||
var dismissing = false
|
||||
|
||||
var startingView: UIView?
|
||||
var endingView: UIView?
|
||||
|
||||
private var shouldPerformZoomingAnimation: Bool {
|
||||
return startingView != nil && endingView != nil
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
setupTransitionContainerHierarchyWithTransitionContext(transitionContext)
|
||||
if shouldPerformZoomingAnimation {
|
||||
performZoomingAnimationWithTransitionContext(transitionContext)
|
||||
}
|
||||
performFadeAnimationWithTransitionContext(transitionContext)
|
||||
}
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return shouldPerformZoomingAnimation ? Settings.animationDurationWithZooming : Settings.animationDurationWithoutZooming
|
||||
}
|
||||
|
||||
private func fadeDurationForTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) -> TimeInterval {
|
||||
let transDuration = transitionDuration(using: transitionContext)
|
||||
return shouldPerformZoomingAnimation ? transDuration * Settings.animationDurationFadeRatio : transDuration
|
||||
}
|
||||
|
||||
private func setupTransitionContainerHierarchyWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
if let toView = transitionContext.view(forKey: UITransitionContextViewKey.to),
|
||||
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) {
|
||||
toView.frame = transitionContext.finalFrame(for: toViewController)
|
||||
let containerView = transitionContext.containerView
|
||||
|
||||
if !toView.isDescendant(of: containerView) {
|
||||
containerView.addSubview(toView)
|
||||
}
|
||||
}
|
||||
|
||||
if dismissing {
|
||||
if let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) {
|
||||
transitionContext.containerView.bringSubview(toFront: fromView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func performZoomingAnimationWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
let containerView = transitionContext.containerView
|
||||
guard let startingView = startingView, let endingView = endingView else { return }
|
||||
|
||||
let startingViewForAnimation = startingView.snapshot
|
||||
let endingViewForAnimation = endingView.snapshot
|
||||
|
||||
let finalEndingViewTransform = endingView.transform
|
||||
let endingViewInitialTransform = startingViewForAnimation.frame.height / endingViewForAnimation.frame.height
|
||||
let startingViewFinalTransform = 1.0 / endingViewInitialTransform
|
||||
|
||||
let translatedStartingViewCenter = startingView.center(inContainerView: containerView)
|
||||
let translatedEndingViewFinalCenter = endingView.center(inContainerView: containerView)
|
||||
|
||||
startingViewForAnimation.center = translatedStartingViewCenter
|
||||
|
||||
endingViewForAnimation.transform = endingViewForAnimation.transform.scaledBy(x: endingViewInitialTransform, y: endingViewInitialTransform)
|
||||
endingViewForAnimation.center = translatedStartingViewCenter
|
||||
endingViewForAnimation.alpha = 0.0
|
||||
|
||||
containerView.addSubview(startingViewForAnimation)
|
||||
containerView.addSubview(endingViewForAnimation)
|
||||
|
||||
endingView.alpha = 0.0
|
||||
startingView.alpha = 0.0
|
||||
|
||||
let transDuration = transitionDuration(using: transitionContext)
|
||||
let fadeInDuration = transDuration * Settings.animationDurationEndingViewFadeInRatio
|
||||
let fadeOutDuration = transDuration * Settings.animationDurationStartingViewFadeOutRatio
|
||||
|
||||
UIView.animate(withDuration: fadeInDuration,
|
||||
delay: 0.0,
|
||||
options: [.allowAnimatedContent,.beginFromCurrentState],
|
||||
animations: { endingViewForAnimation.alpha = 1.0 },
|
||||
completion: { _ in
|
||||
UIView.animate(withDuration: fadeOutDuration,
|
||||
delay: 0.0,
|
||||
options: [.allowAnimatedContent,.beginFromCurrentState],
|
||||
animations: { startingViewForAnimation.alpha = 0.0 },
|
||||
completion: { _ in
|
||||
startingViewForAnimation.removeFromSuperview()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
UIView.animate(withDuration: transDuration,
|
||||
delay: 0.0,
|
||||
usingSpringWithDamping:Settings.zoomingAnimationSpringDamping,
|
||||
initialSpringVelocity:0,
|
||||
options: [.allowAnimatedContent,.beginFromCurrentState],
|
||||
animations: { () -> Void in
|
||||
endingViewForAnimation.transform = finalEndingViewTransform
|
||||
endingViewForAnimation.center = translatedEndingViewFinalCenter
|
||||
startingViewForAnimation.transform = startingViewForAnimation.transform.scaledBy(x: startingViewFinalTransform, y: startingViewFinalTransform)
|
||||
startingViewForAnimation.center = translatedEndingViewFinalCenter
|
||||
},
|
||||
completion: { [weak self] _ in
|
||||
endingViewForAnimation.removeFromSuperview()
|
||||
endingView.alpha = 1.0
|
||||
startingView.alpha = 1.0
|
||||
self?.completeTransitionWithTransitionContext(transitionContext)
|
||||
})
|
||||
}
|
||||
|
||||
private func performFadeAnimationWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
let fadeView = dismissing ? transitionContext.view(forKey: UITransitionContextViewKey.from) : transitionContext.view(forKey: UITransitionContextViewKey.to)
|
||||
let beginningAlpha: CGFloat = dismissing ? 1.0 : 0.0
|
||||
let endingAlpha: CGFloat = dismissing ? 0.0 : 1.0
|
||||
|
||||
fadeView?.alpha = beginningAlpha
|
||||
|
||||
UIView.animate(withDuration: fadeDurationForTransitionContext(transitionContext), animations: {
|
||||
fadeView?.alpha = endingAlpha
|
||||
}) { finished in
|
||||
if !self.shouldPerformZoomingAnimation {
|
||||
self.completeTransitionWithTransitionContext(transitionContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func completeTransitionWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
if transitionContext.isInteractive {
|
||||
if transitionContext.transitionWasCancelled {
|
||||
transitionContext.cancelInteractiveTransition()
|
||||
} else {
|
||||
transitionContext.finishInteractiveTransition()
|
||||
}
|
||||
}
|
||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
import UIKit
|
||||
|
||||
@objc(MWMPhotosViewController)
|
||||
final class PhotosViewController: MWMViewController {
|
||||
var referenceViewForPhotoWhenDismissingHandler: ((GalleryItemModel) -> UIView?)?
|
||||
|
||||
private let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [UIPageViewControllerOptionInterPageSpacingKey: 16.0])
|
||||
private(set) var photos: GalleryModel
|
||||
|
||||
fileprivate let transitionAnimator = PhotosTransitionAnimator()
|
||||
fileprivate let interactiveAnimator = PhotosInteractionAnimator()
|
||||
fileprivate let overlayView = PhotosOverlayView(frame: CGRect.zero)
|
||||
private var overlayViewHidden = false
|
||||
|
||||
private lazy var singleTapGestureRecognizer: UITapGestureRecognizer = {
|
||||
return UITapGestureRecognizer(target: self, action: #selector(handleSingleTapGestureRecognizer(_:)))
|
||||
}()
|
||||
private lazy var panGestureRecognizer: UIPanGestureRecognizer = {
|
||||
return UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer(_:)))
|
||||
}()
|
||||
|
||||
fileprivate var interactiveDismissal = false
|
||||
|
||||
private var currentPhotoViewController: PhotoViewController? {
|
||||
return pageViewController.viewControllers?.first as? PhotoViewController
|
||||
}
|
||||
|
||||
fileprivate var currentPhoto: GalleryItemModel? {
|
||||
return currentPhotoViewController?.photo
|
||||
}
|
||||
|
||||
init(photos: GalleryModel, initialPhoto: GalleryItemModel? = nil, referenceView: UIView? = nil) {
|
||||
self.photos = photos
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
initialSetupWithInitialPhoto(initialPhoto)
|
||||
transitionAnimator.startingView = referenceView
|
||||
transitionAnimator.endingView = currentPhotoViewController?.scalingView.imageView
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
fileprivate func initialSetupWithInitialPhoto(_ initialPhoto: GalleryItemModel? = nil) {
|
||||
overlayView.photosViewController = self
|
||||
setupPageViewController(initialPhoto: initialPhoto)
|
||||
|
||||
modalPresentationStyle = .custom
|
||||
transitioningDelegate = self
|
||||
modalPresentationCapturesStatusBarAppearance = true
|
||||
|
||||
overlayView.photosViewController = self
|
||||
}
|
||||
|
||||
private func setupPageViewController(initialPhoto: GalleryItemModel? = nil) {
|
||||
pageViewController.view.backgroundColor = UIColor.clear
|
||||
pageViewController.delegate = self
|
||||
pageViewController.dataSource = self
|
||||
|
||||
if let photo = initialPhoto {
|
||||
let photoViewController = initializePhotoViewController(photo: photo)
|
||||
pageViewController.setViewControllers([photoViewController],
|
||||
direction: .forward,
|
||||
animated: false,
|
||||
completion: nil)
|
||||
}
|
||||
overlayView.photo = initialPhoto
|
||||
}
|
||||
|
||||
fileprivate func initializePhotoViewController(photo: GalleryItemModel) -> PhotoViewController {
|
||||
let photoViewController = PhotoViewController(photo: photo)
|
||||
singleTapGestureRecognizer.require(toFail: photoViewController.doubleTapGestureRecognizer)
|
||||
return photoViewController
|
||||
}
|
||||
|
||||
// MARK: - View Life Cycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = UIColor.white
|
||||
view.backgroundColor = UIColor.black
|
||||
pageViewController.view.backgroundColor = UIColor.clear
|
||||
|
||||
pageViewController.view.addGestureRecognizer(singleTapGestureRecognizer)
|
||||
pageViewController.view.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
addChildViewController(pageViewController)
|
||||
view.addSubview(pageViewController.view)
|
||||
pageViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
pageViewController.didMove(toParentViewController: self)
|
||||
|
||||
setupOverlayView()
|
||||
}
|
||||
|
||||
private func setupOverlayView() {
|
||||
overlayView.photo = currentPhoto
|
||||
overlayView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
overlayView.frame = view.bounds
|
||||
view.addSubview(overlayView)
|
||||
setOverlayHidden(false, animated: false)
|
||||
}
|
||||
|
||||
private func setOverlayHidden(_ hidden: Bool, animated: Bool) {
|
||||
overlayViewHidden = hidden
|
||||
overlayView.setHidden(hidden, animated: animated) { [weak self] in
|
||||
self?.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
guard presentedViewController == nil else {
|
||||
super.dismiss(animated: flag, completion: completion)
|
||||
return
|
||||
}
|
||||
transitionAnimator.startingView = currentPhotoViewController?.scalingView.imageView
|
||||
if let currentPhoto = currentPhoto {
|
||||
transitionAnimator.endingView = referenceViewForPhotoWhenDismissingHandler?(currentPhoto)
|
||||
} else {
|
||||
transitionAnimator.endingView = nil
|
||||
}
|
||||
let overlayWasHiddenBeforeTransition = overlayView.isHidden
|
||||
setOverlayHidden(true, animated: true)
|
||||
|
||||
super.dismiss(animated: flag) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
let isStillOnscreen = s.view.window != nil
|
||||
if isStillOnscreen && !overlayWasHiddenBeforeTransition {
|
||||
s.setOverlayHidden(false, animated: true)
|
||||
}
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Gesture Recognizers
|
||||
@objc
|
||||
private func handlePanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||
if gestureRecognizer.state == .began {
|
||||
interactiveDismissal = true
|
||||
dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
interactiveDismissal = false
|
||||
interactiveAnimator.handlePanWithPanGestureRecognizer(gestureRecognizer, viewToPan: pageViewController.view, anchorPoint: CGPoint(x: view.bounds.midX, y: view.bounds.midY))
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
private func handleSingleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
setOverlayHidden(!overlayView.isHidden, animated: true)
|
||||
}
|
||||
|
||||
//MARK: - Orientations
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return .all
|
||||
}
|
||||
|
||||
// MARK: - Status Bar
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
return overlayViewHidden
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .lightContent
|
||||
}
|
||||
|
||||
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||
return .fade
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotosViewController: UIViewControllerTransitioningDelegate {
|
||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
transitionAnimator.dismissing = false
|
||||
return transitionAnimator
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
transitionAnimator.dismissing = true
|
||||
return transitionAnimator
|
||||
}
|
||||
|
||||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
if interactiveDismissal {
|
||||
interactiveAnimator.animator = transitionAnimator
|
||||
interactiveAnimator.shouldAnimateUsingAnimator = transitionAnimator.endingView != nil
|
||||
interactiveAnimator.viewToHideWhenBeginningTransition = overlayView
|
||||
return interactiveAnimator
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotosViewController: UIPageViewControllerDataSource {
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||
guard let photoViewController = viewController as? PhotoViewController,
|
||||
let photoIndex = photos.items.index(where: { $0 === photoViewController.photo }),
|
||||
photoIndex - 1 >= 0 else {
|
||||
return nil
|
||||
}
|
||||
let newPhoto = photos.items[photoIndex - 1]
|
||||
return initializePhotoViewController(photo: newPhoto)
|
||||
}
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
||||
guard let photoViewController = viewController as? PhotoViewController,
|
||||
let photoIndex = photos.items.index(where: { $0 === photoViewController.photo }),
|
||||
photoIndex + 1 < photos.items.count else {
|
||||
return nil
|
||||
}
|
||||
let newPhoto = photos.items[photoIndex + 1]
|
||||
return initializePhotoViewController(photo: newPhoto)
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotosViewController: UIPageViewControllerDelegate {
|
||||
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
|
||||
if completed {
|
||||
if let currentPhoto = currentPhoto {
|
||||
overlayView.photo = currentPhoto
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue