我想使用垂直导航在我的应用程序中使用漫游动画,使用此tutorial中的以下代码我可以水平导航。我想知道我应该对垂直穿行做出哪些改变。有人可以帮忙吗?提前谢谢。
BWWalkthroughPageViewController.swift
import UIKit
public enum WalkthroughAnimationType:String{
case Linear = "Linear"
case Curve = "Curve"
case Zoom = "Zoom"
case InOut = "InOut"
init(_ name:String){
if let tempSelf = WalkthroughAnimationType(rawValue: name){
self = tempSelf
}else{
self = .Linear
}
}
}
open class BWWalkthroughPageViewController: UIViewController, BWWalkthroughPage {
fileprivate var animation:WalkthroughAnimationType = .Linear
fileprivate var subsWeights:[CGPoint] = Array()
fileprivate var notAnimatableViews:[Int] = [] // Array of views' tags that should not be animated during the scroll/transition
// MARK: Inspectable Properties
// Edit these values using the Attribute inspector or modify directly the "User defined runtime attributes" in IB
@IBInspectable var speed:CGPoint = CGPoint(x: 0.0, y: 0.0); // Note if you set this value via Attribute inspector it can only be an Integer (change it manually via User defined runtime attribute if you need a Float)
@IBInspectable var speedVariance:CGPoint = CGPoint(x: 0.0, y: 0.0) // Note if you set this value via Attribute inspector it can only be an Integer (change it manually via User defined runtime attribute if you need a Float)
@IBInspectable var animationType:String {
set(value){
self.animation = WalkthroughAnimationType(rawValue: value)!
}
get{
return self.animation.rawValue
}
}
@IBInspectable var animateAlpha:Bool = false
@IBInspectable var staticTags:String { // A comma separated list of tags that you don't want to animate during the transition/scroll
set(value){
self.notAnimatableViews = value.components(separatedBy: ",").map{Int($0)!}
}
get{
return notAnimatableViews.map{String($0)}.joined(separator: ",")
}
}
// MARK: BWWalkthroughPage Implementation
override open func viewDidLoad() {
super.viewDidLoad()
self.view.layer.masksToBounds = true
subsWeights = Array()
for v in view.subviews{
speed.x += speedVariance.x
speed.y += speedVariance.y
if !notAnimatableViews.contains(v.tag) {
subsWeights.append(speed)
}
}
}
open func walkthroughDidScroll(_ position: CGFloat, offset: CGFloat) {
for i in 0 ..< subsWeights.count
{
// Perform Transition/Scale/Rotate animations
switch animation
{
case .Linear:
animationLinear(i, offset)
case .Zoom:
animationZoom(i, offset)
case .Curve:
animationCurve(i, offset)
case .InOut:
animationInOut(i, offset)
}
// Animate alpha
if(animateAlpha)
{
animationAlpha(i, offset)
}
}
}
// MARK: Animations (WIP)
private func animationAlpha(_ index:Int, _ offset:CGFloat){
var offset = offset
let cView = view.subviews[index]
if(offset > 1.0){
offset = 1.0 + (1.0 - offset)
}
cView.alpha = (offset)
}
fileprivate func animationCurve(_ index:Int, _ offset:CGFloat){
var transform = CATransform3DIdentity
let x:CGFloat = (1.0 - offset) * 10
transform = CATransform3DTranslate(transform, (pow(x,3) - (x * 25)) * subsWeights[index].x, (pow(x,3) - (x * 20)) * subsWeights[index].y, 0 )
applyTransform(index, transform: transform)
}
fileprivate func animationZoom(_ index:Int, _ offset:CGFloat){
var transform = CATransform3DIdentity
var tmpOffset = offset
if(tmpOffset > 1.0){
tmpOffset = 1.0 + (1.0 - tmpOffset)
}
let scale:CGFloat = (1.0 - tmpOffset)
transform = CATransform3DScale(transform, 1 - scale , 1 - scale, 1.0)
applyTransform(index, transform: transform)
}
fileprivate func animationLinear(_ index:Int, _ offset:CGFloat){
var transform = CATransform3DIdentity
let mx:CGFloat = (1.0 - offset) * 100
transform = CATransform3DTranslate(transform, mx * subsWeights[index].x, mx * subsWeights[index].y, 0 )
applyTransform(index, transform: transform)
}
fileprivate func animationInOut(_ index:Int, _ offset:CGFloat){
var transform = CATransform3DIdentity
//var x:CGFloat = (1.0 - offset) * 20
var tmpOffset = offset
if(tmpOffset > 1.0){
tmpOffset = 1.0 + (1.0 - tmpOffset)
}
transform = CATransform3DTranslate(transform, (1.0 - tmpOffset) * subsWeights[index].x * 100, (1.0 - tmpOffset) * subsWeights[index].y * 100, 0)
applyTransform(index, transform: transform)
}
fileprivate func applyTransform(_ index:Int, transform:CATransform3D){
let subview = view.subviews[index]
if !notAnimatableViews.contains(subview.tag){
view.subviews[index].layer.transform = transform
}
}
}
BWWalkthroughViewController.swift:
import UIKit
// MARK: - Protocols -
/**
Walkthrough Delegate:
This delegate performs basic operations such as dismissing the Walkthrough or call whatever action on page change.
Probably the Walkthrough is presented by this delegate.
**/
@objc public protocol BWWalkthroughViewControllerDelegate{
@objc optional func walkthroughCloseButtonPressed() // If the skipRequest(sender:) action is connected to a button, this function is called when that button is pressed.
@objc optional func walkthroughNextButtonPressed() //
@objc optional func walkthroughPrevButtonPressed() //
@objc optional func walkthroughPageDidChange(_ pageNumber:Int) // Called when current page changes
}
/**
Walkthrough Page:
The walkthrough page represents any page added to the Walkthrough.
At the moment it's only used to perform custom animations on didScroll.
**/
@objc public protocol BWWalkthroughPage
{
// While sliding to the "next" slide (from right to left), the "current" slide changes its offset from 1.0 to 2.0 while the "next" slide changes it from 0.0 to 1.0
// While sliding to the "previous" slide (left to right), the current slide changes its offset from 1.0 to 0.0 while the "previous" slide changes it from 2.0 to 1.0
// The other pages update their offsets whith values like 2.0, 3.0, -2.0... depending on their positions and on the status of the walkthrough
// This value can be used on the previous, current and next page to perform custom animations on page's subviews.
@objc func walkthroughDidScroll(_ position:CGFloat, offset:CGFloat) // Called when the main Scrollview...scrolls
}
@objc open class BWWalkthroughViewController: UIViewController, UIScrollViewDelegate{
// MARK: - Public properties -
weak open var delegate:BWWalkthroughViewControllerDelegate?
// TODO: If you need a page control, next or prev buttons add them via IB and connect them with these Outlets
@IBOutlet open var pageControl:UIPageControl?
@IBOutlet open var nextButton:UIButton?
@IBOutlet open var prevButton:UIButton?
@IBOutlet open var closeButton:UIButton?
open var currentPage:Int{ // The index of the current page (readonly)
get{
let page = Int((scrollview.contentOffset.x / view.bounds.size.width))
return page
}
}
open var currentViewController:UIViewController{ //the controller for the currently visible page
get{
let currentPage = self.currentPage;
return controllers[currentPage];
}
}
open var numberOfPages:Int{ //the total number of pages in the walkthrough
get{
return self.controllers.count;
}
}
// MARK: - Private properties -
open let scrollview:UIScrollView!
fileprivate var controllers:[UIViewController]!
fileprivate var lastViewConstraint:NSArray?
// MARK: - Overrides -
required public init?(coder aDecoder: NSCoder) {
// Setup the scrollview
scrollview = UIScrollView()
scrollview.showsHorizontalScrollIndicator = false
scrollview.showsVerticalScrollIndicator = false
scrollview.isPagingEnabled = true
// Controllers as empty array
controllers = Array()
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?){
scrollview = UIScrollView()
controllers = Array()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override open func viewDidLoad() {
super.viewDidLoad()
// Initialize UI Elements
pageControl?.addTarget(self, action: #selector(BWWalkthroughViewController.pageControlDidTouch), for: UIControlEvents.touchUpInside)
// Scrollview
scrollview.delegate = self
scrollview.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(scrollview, at: 0) //scrollview is inserted as first view of the hierarchy
// Set scrollview related constraints
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[scrollview]-0-|", options:[], metrics: nil, views: ["scrollview":scrollview]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[scrollview]-0-|", options:[], metrics: nil, views: ["scrollview":scrollview]))
}
override open func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
pageControl?.numberOfPages = controllers.count
pageControl?.currentPage = 0
}
// MARK: - Internal methods -
/**
* Progresses to the next page, or calls the finished delegate method if already on the last page
*/
@IBAction open func nextPage(){
if (currentPage + 1) < controllers.count {
delegate?.walkthroughNextButtonPressed?()
gotoPage(currentPage + 1)
}
}
@IBAction open func prevPage(){
if currentPage > 0 {
delegate?.walkthroughPrevButtonPressed?()
gotoPage(currentPage - 1)
}
}
// TODO: If you want to implement a "skip" button
// connect the button to this IBAction and implement the delegate with the skipWalkthrough
@IBAction open func close(_ sender: AnyObject){
delegate?.walkthroughCloseButtonPressed?()
}
func pageControlDidTouch(){
if let pc = pageControl{
gotoPage(pc.currentPage)
}
}
fileprivate func gotoPage(_ page:Int){
if page < controllers.count{
var frame = scrollview.frame
frame.origin.x = CGFloat(page) * frame.size.width
scrollview.scrollRectToVisible(frame, animated: true)
}
}
/**
addViewController
Add a new page to the walkthrough.
To have information about the current position of the page in the walkthrough add a UIVIewController which implements BWWalkthroughPage
*/
open func addViewController(_ vc:UIViewController)->Void{
controllers.append(vc)
// Setup the viewController view
vc.view.translatesAutoresizingMaskIntoConstraints = false
scrollview.addSubview(vc.view)
// Constraints
let metricDict = ["w":vc.view.bounds.size.width,"h":vc.view.bounds.size.height]
// - Generic cnst
vc.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view(h)]", options:[], metrics: metricDict, views: ["view":vc.view]))
vc.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[view(w)]", options:[], metrics: metricDict, views: ["view":vc.view]))
scrollview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]|", options:[], metrics: nil, views: ["view":vc.view,]))
// cnst for position: 1st element
if controllers.count == 1{
scrollview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]", options:[], metrics: nil, views: ["view":vc.view,]))
// cnst for position: other elements
}else{
let previousVC = controllers[controllers.count-2]
let previousView = previousVC.view;
scrollview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[previousView]-0-[view]", options:[], metrics: nil, views: ["previousView":previousView!,"view":vc.view]))
if let cst = lastViewConstraint{
scrollview.removeConstraints(cst as! [NSLayoutConstraint])
}
lastViewConstraint = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-0-|", options:[], metrics: nil, views: ["view":vc.view]) as NSArray
scrollview.addConstraints(lastViewConstraint! as! [NSLayoutConstraint])
}
}
/**
Update the UI to reflect the current walkthrough status
**/
fileprivate func updateUI(){
// Get the current page
pageControl?.currentPage = currentPage
// Notify delegate about the new page
delegate?.walkthroughPageDidChange?(currentPage)
// Hide/Show navigation buttons
if currentPage == controllers.count - 1{
nextButton?.isHidden = true
}else{
nextButton?.isHidden = false
}
if currentPage == 0{
prevButton?.isHidden = true
}else{
prevButton?.isHidden = false
}
}
// MARK: - Scrollview Delegate -
open func scrollViewDidScroll(_ sv: UIScrollView) {
for i in 0 ..< controllers.count {
if let vc = controllers[i] as? BWWalkthroughPage{
let mx = ((scrollview.contentOffset.x + view.bounds.size.width) - (view.bounds.size.width * CGFloat(i))) / view.bounds.size.width
// While sliding to the "next" slide (from right to left), the "current" slide changes its offset from 1.0 to 2.0 while the "next" slide changes it from 0.0 to 1.0
// While sliding to the "previous" slide (left to right), the current slide changes its offset from 1.0 to 0.0 while the "previous" slide changes it from 2.0 to 1.0
// The other pages update their offsets whith values like 2.0, 3.0, -2.0... depending on their positions and on the status of the walkthrough
// This value can be used on the previous, current and next page to perform custom animations on page's subviews.
// print the mx value to get more info.
// println("\(i):\(mx)")
// We animate only the previous, current and next page
if(mx < 2 && mx > -2.0){
vc.walkthroughDidScroll(scrollview.contentOffset.x, offset: mx)
}
}
}
}
open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
updateUI()
}
open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
updateUI()
}
/* WIP */
override open func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
print("CHANGE")
}
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
print("SIZE")
}
}