草庐IT

swift - Swift iPad 中的侧面板

coder 2023-09-06 原文

我的 iPad 应用侧面板有问题。我需要如下堆叠的按钮:

预期输出:

现在,我的输出产生:

当前输出:

如何删除圆圈并添加按钮组?

import UIKit
import QuartzCore


public protocol FrostedSidebarDelegate{

    func sidebar(sidebar: FrostedSidebar, willShowOnScreenAnimated animated: Bool)

    func sidebar(sidebar: FrostedSidebar, didShowOnScreenAnimated animated: Bool)

    func sidebar(sidebar: FrostedSidebar, willDismissFromScreenAnimated animated: Bool)

    func sidebar(sidebar: FrostedSidebar, didDismissFromScreenAnimated animated: Bool)

    func sidebar(sidebar: FrostedSidebar, didTapItemAtIndex index: Int)


    func sidebar(sidebar: FrostedSidebar, didEnable itemEnabled: Bool, itemAtIndex index: Int)
}


var sharedSidebar: FrostedSidebar?


public enum SidebarItemSelectionStyle{

    case None
    se Single

    case All
}


public class FrostedSidebar: UIViewController {


    public var width:                   CGFloat                     = 300.0
    /**
     If the sidebar should show from the right.
    */
    public var showFromRight:           Bool                        = false
    /**
     The speed at which the sidebar is presented/dismissed.
    */
    public var animationDuration:       CGFloat                     = 0.25
    /**
     The size of the sidebar items.
    */
    public var itemSize:                CGSize                      = CGSize(width: 200.0, height: 200.0)
    /**
     The background color of the sidebar items.
    */
    public var itemBackgroundColor:     UIColor                     = UIColor(white: 1, alpha: 0.25)
    /**
     The width of the ring around selected sidebar items.
    */
    public var borderWidth:             CGFloat                     = 2
    /**
     The sidebar's delegate.
    */
    public var delegate:                FrostedSidebarDelegate?     = nil
    /**
     A dictionary that holds the actions for each item index.
    */
    public var actionForIndex:          [Int : ()->()]              = [:]
    /**
     The indexes that are selected and have rings around them.
    */
    public var selectedIndices:         NSMutableIndexSet           = NSMutableIndexSet()
    /**
     If the sidebar should be positioned beneath a navigation bar that is on screen.
    */
    public var adjustForNavigationBar:  Bool                        = false
    /**
     Returns whether or not the sidebar is currently being displayed
    */
    public var isCurrentlyOpen:         Bool                        = false
    /**
     The selection style for the sidebar.
    */
    public var selectionStyle:          SidebarItemSelectionStyle   = .None{
        didSet{
            if case .All = selectionStyle{
                selectedIndices = NSMutableIndexSet(indexesInRange: NSRange(location: 0, length: images.count))
            }
        }
    }

    //MARK: Private Properties

    private var contentView:            UIScrollView                = UIScrollView()
    private var blurView:               UIVisualEffectView          = UIVisualEffectView(effect: UIBlurEffect(style: .Dark))
    private var dimView:                UIView                      = UIView()
    private var tapGesture:             UITapGestureRecognizer?     = nil
    private var images:                 [UIImage]                   = []
    private var borderColors:           [UIColor]?                  = nil
    private var itemViews:              [CalloutItem]               = []

    //MARK: Public Methods

    /**
     Returns an object initialized from data in a given unarchiver.
    */
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }


    /**
     Returns a sidebar initialized with the given data.

     - Parameter itemImages: The images that will be used for each item.
     - Parameter colors: The color of rings around each image.
     - Parameter selectionStyle: The selection style for the sidebar.

     - Precondition: `colors` is either `nil` or contains the same number of elements as `itemImages`.
    */
    public init(itemImages: [UIImage], colors: [UIColor]?, selectionStyle: SidebarItemSelectionStyle){
        contentView.alwaysBounceHorizontal = false
        contentView.alwaysBounceVertical = true
        contentView.bounces = true
        contentView.clipsToBounds = false
        contentView.showsHorizontalScrollIndicator = false
        contentView.showsVerticalScrollIndicator = false
        if let colors = colors{
            assert(itemImages.count == colors.count, "If item color are supplied, the itemImages and colors arrays must be of the same size.")
        }

        self.selectionStyle = selectionStyle
        borderColors = colors
        images = itemImages

        for (index, image) in images.enumerate(){
            let view = CalloutItem(index: index)
            view.clipsToBounds = true
            view.imageView.image = image
            contentView.addSubview(view)
            itemViews += [view]
            if let borderColors = borderColors{
                if selectedIndices.containsIndex(index){
                    let color = borderColors[index]
                    view.layer.borderColor = color.CGColor
                }
            } else{
                view.layer.borderColor = UIColor.clearColor().CGColor
            }
        }

        super.init(nibName: nil, bundle: nil)
    }

    public override func shouldAutorotate() -> Bool {
        return true
    }

    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.All
    }

    public override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
        if isViewLoaded(){
            dismissAnimated(false, completion: nil)
        }
    }

    public override func loadView() {
        super.loadView()
        view.backgroundColor = UIColor.clearColor()
        view.addSubview(dimView)
        view.addSubview(blurView)
        view.addSubview(contentView)
        tapGesture = UITapGestureRecognizer(target: self, action: #selector(FrostedSidebar.handleTap(_:)))
        view.addGestureRecognizer(tapGesture!)
    }

    /**
     Shows the sidebar in a view controller.

     - Parameter viewController: The view controller in which to show the sidebar.
     - Parameter animated: If the sidebar should be animated.
    */
    public func showInViewController(viewController: UIViewController, animated: Bool){
        layoutItems()
        if let bar = sharedSidebar{
            bar.dismissAnimated(false, completion: nil)
        }

        delegate?.sidebar(self, willShowOnScreenAnimated: animated)

        sharedSidebar = self

        addToParentViewController(viewController, callingAppearanceMethods: true)
        view.frame = viewController.view.bounds

        dimView.backgroundColor = UIColor.blackColor()
        dimView.alpha = 0
        dimView.frame = view.bounds

        let parentWidth = view.bounds.size.width
        var contentFrame = view.bounds
        contentFrame.origin.x = showFromRight ? parentWidth : -width
        contentFrame.size.width = width
        contentView.frame = contentFrame
        contentView.contentOffset = CGPoint(x: 0, y: 0)
        layoutItems()

        var blurFrame = CGRect(x: showFromRight ? view.bounds.size.width : 0, y: 0, width: 0, height: view.bounds.size.height)
        blurView.frame = blurFrame
        blurView.contentMode = showFromRight ? UIViewContentMode.TopRight : UIViewContentMode.TopLeft
        blurView.clipsToBounds = true
        view.insertSubview(blurView, belowSubview: contentView)

        contentFrame.origin.x = showFromRight ? parentWidth - width : 0
        blurFrame.origin.x = contentFrame.origin.x
        blurFrame.size.width = width

        let animations: () -> () = {
            self.contentView.frame = contentFrame
            self.blurView.frame = blurFrame
            self.dimView.alpha = 0.25
        }
        let completion: (Bool) -> Void = { finished in
            if finished{
                self.delegate?.sidebar(self, didShowOnScreenAnimated: animated)
            }
        }

        if animated{
            UIView.animateWithDuration(NSTimeInterval(animationDuration), delay: 0, options: UIViewAnimationOptions(), animations: animations, completion: completion)
        } else{
            animations()
            completion(true)
        }

        for (index, item) in itemViews.enumerate(){
            item.layer.transform = CATransform3DMakeScale(0.3, 0.3, 1)
            item.alpha = 0
            item.originalBackgroundColor = itemBackgroundColor
            item.layer.borderWidth = borderWidth
            animateSpringWithView(item, idx: index, initDelay: animationDuration)
        }

         self.isCurrentlyOpen = true
    }

    /**
     Dismisses the sidebar.

     - Parameter animated: If the sidebar should be animated.
     - Parameter completion: Completion handler called when the sidebar is dismissed.
    */
    public func dismissAnimated(animated: Bool, completion: ((Bool) -> Void)?){
        let completionBlock: (Bool) -> Void = {finished in
            self.removeFromParentViewControllerCallingAppearanceMethods(true)
            self.delegate?.sidebar(self, didDismissFromScreenAnimated: true)
            self.layoutItems()
            if let completion = completion{
                completion(finished)
            }
        }
        delegate?.sidebar(self, willDismissFromScreenAnimated: animated)
        if animated{
            let parentWidth = view.bounds.size.width
            var contentFrame = contentView.frame
            contentFrame.origin.x = showFromRight ? parentWidth : -width
            var blurFrame = blurView.frame
            blurFrame.origin.x = showFromRight ? parentWidth : 0
            blurFrame.size.width = 0
            UIView.animateWithDuration(NSTimeInterval(animationDuration), delay: 0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: {
                self.contentView.frame = contentFrame
                self.blurView.frame = blurFrame
                self.dimView.alpha = 0
                }, completion: completionBlock)
        } else{
            completionBlock(true)
        }

        self.isCurrentlyOpen = false
    }

    /**
     Selects the item at the given index.

     - Parameter index: The index of the item to select.
    */
    public func selectItemAtIndex(index: Int){
        let didEnable = !selectedIndices.containsIndex(index)
        if let borderColors = borderColors{
            let stroke = borderColors[index]
            let item = itemViews[index]
            if didEnable{
                if case .Single = selectionStyle{
                    selectedIndices.removeAllIndexes()
                    for item in itemViews{
                        item.layer.borderColor = UIColor.clearColor().CGColor
                    }
                }
                item.layer.borderColor = stroke.CGColor

                let borderAnimation = CABasicAnimation(keyPath: "borderColor")
                borderAnimation.fromValue = UIColor.clearColor().CGColor
                borderAnimation.toValue = stroke.CGColor
                borderAnimation.duration = 0.5
                item.layer.addAnimation(borderAnimation, forKey: nil)
                selectedIndices.addIndex(index)

            } else{
                if case .None = selectionStyle{
                        item.layer.borderColor = UIColor.clearColor().CGColor
                        selectedIndices.removeIndex(index)
                }
            }
            let pathFrame = CGRect(x: -CGRectGetMidX(item.bounds), y: -CGRectGetMidY(item.bounds), width: item.bounds.size.width, height: item.bounds.size.height)
            let path = UIBezierPath(roundedRect: pathFrame, cornerRadius: item.layer.cornerRadius)
            let shapePosition = view.convertPoint(item.center, fromView: contentView)
            let circleShape = CAShapeLayer()
            circleShape.path = path.CGPath
            circleShape.position = shapePosition
            circleShape.fillColor = UIColor.clearColor().CGColor
            circleShape.opacity = 0
            circleShape.strokeColor = stroke.CGColor
            circleShape.lineWidth = borderWidth
            view.layer.addSublayer(circleShape)

            let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
            scaleAnimation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
            scaleAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeScale(2.5, 2.5, 1))
            let alphaAnimation = CABasicAnimation(keyPath: "opacity")
            alphaAnimation.fromValue = 1
            alphaAnimation.toValue = 0
            let animation = CAAnimationGroup()
            animation.animations = [scaleAnimation, alphaAnimation]
            animation.duration = 0.5
            animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            circleShape.addAnimation(animation, forKey: nil)
        }
        if let action = actionForIndex[index]{
            action()
        }
        delegate?.sidebar(self, didTapItemAtIndex: index)
        delegate?.sidebar(self, didEnable: didEnable, itemAtIndex: index)
    }

    //MARK: Private Classes

    private class CalloutItem: UIView{
        var imageView:              UIImageView                 = UIImageView()
        var itemIndex:              Int
        var originalBackgroundColor:UIColor? {
            didSet{
                backgroundColor = originalBackgroundColor
            }
        }

        required init?(coder aDecoder: NSCoder) {
            itemIndex = 0
            super.init(coder: aDecoder)
        }

        init(index: Int){
            imageView.backgroundColor = UIColor.clearColor()
            imageView.contentMode = UIViewContentMode.ScaleAspectFit
            itemIndex = index
            super.init(frame: CGRect.zero)
            addSubview(imageView)
        }

        override func layoutSubviews() {
            super.layoutSubviews()
            let inset: CGFloat = bounds.size.height/2
            imageView.frame = CGRect(x: 0, y: 0, width: inset, height: inset)
            imageView.center = CGPoint(x: inset, y: inset)
        }

        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            super.touchesBegan(touches, withEvent: event)

            var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
            let darkenFactor: CGFloat = 0.3
            var darkerColor: UIColor
            if originalBackgroundColor != nil && originalBackgroundColor!.getRed(&r, green: &g, blue: &b, alpha: &a){
                darkerColor = UIColor(red: max(r - darkenFactor, 0), green: max(g - darkenFactor, 0), blue: max(b - darkenFactor, 0), alpha: a)
            } else if originalBackgroundColor != nil && originalBackgroundColor!.getWhite(&r, alpha: &a){
                darkerColor = UIColor(white: max(r - darkenFactor, 0), alpha: a)
            } else{
                darkerColor = UIColor.clearColor()
                assert(false, "Item color should be RBG of White/Alpha in order to darken the button")
            }
            backgroundColor = darkerColor
        }

        override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
            super.touchesEnded(touches, withEvent: event)
            backgroundColor = originalBackgroundColor
        }

        override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
            super.touchesCancelled(touches, withEvent: event)
            backgroundColor = originalBackgroundColor
        }

    }

    //MARK: Private Methods

    private func animateSpringWithView(view: CalloutItem, idx: Int, initDelay: CGFloat){
        let delay: NSTimeInterval = NSTimeInterval(initDelay) + NSTimeInterval(idx) * 0.1
        UIView.animateWithDuration(0.5,
            delay: delay,
            usingSpringWithDamping: 10.0,
            initialSpringVelocity: 50.0,
            options: UIViewAnimationOptions.BeginFromCurrentState,
            animations: {
                view.layer.transform = CATransform3DIdentity
                view.alpha = 1
            },
            completion: nil)
    }

    @objc private func handleTap(recognizer: UITapGestureRecognizer){
        let location = recognizer.locationInView(view)
        if !CGRectContainsPoint(contentView.frame, location){
            dismissAnimated(true, completion: nil)
        } else{
            let tapIndex = indexOfTap(recognizer.locationInView(contentView))
            if let tapIndex = tapIndex{
                selectItemAtIndex(tapIndex)
            }
        }
    }

    private func layoutSubviews(){
        let x = showFromRight ? parentViewController!.view.bounds.size.width - width : 0
        contentView.frame = CGRect(x: x, y: 0, width: width, height: parentViewController!.view.bounds.size.height)
        blurView.frame = contentView.frame
        layoutItems()
    }

    private func layoutItems(){
        let leftPadding: CGFloat = (width - itemSize.width) / 2
        let topPadding: CGFloat = leftPadding
        for (index, item) in itemViews.enumerate(){
            let idx: CGFloat = adjustForNavigationBar ? CGFloat(index) + 0.5 : CGFloat(index)

            let frame = CGRect(x: leftPadding, y: topPadding*idx + itemSize.height*idx + topPadding, width:itemSize.width, height: itemSize.height)
            item.frame = frame
            item.layer.cornerRadius = frame.size.width / 2
            item.layer.borderColor = UIColor.clearColor().CGColor
            item.alpha = 0
            if selectedIndices.containsIndex(index){
                if let borderColors = borderColors{
                    item.layer.borderColor = borderColors[index].CGColor
                }
            }
        }
        let itemCount = CGFloat(itemViews.count)
        if adjustForNavigationBar{
            contentView.contentSize = CGSizeMake(0, (itemCount + 0.5) * (itemSize.height + topPadding) + topPadding)
        } else {
            contentView.contentSize = CGSizeMake(0, itemCount * (itemSize.height + topPadding) + topPadding)
        }
    }

    private func indexOfTap(location: CGPoint) -> Int? {
        var index: Int?
        for (idx, item) in itemViews.enumerate(){
            if CGRectContainsPoint(item.frame, location){
                index = idx
                break
            }
        }
        return index
    }

    private func addToParentViewController(viewController: UIViewController, callingAppearanceMethods: Bool){
        if let _ = parentViewController{
            removeFromParentViewControllerCallingAppearanceMethods(callingAppearanceMethods)
        }
        if callingAppearanceMethods{
            beginAppearanceTransition(true, animated: false)
        }
        viewController.addChildViewController(self)
        viewController.view.addSubview(view)
        didMoveToParentViewController(self)
        if callingAppearanceMethods{
            endAppearanceTransition()
        }
    }

    private func removeFromParentViewControllerCallingAppearanceMethods(callAppearanceMethods: Bool){

        if callAppearanceMethods{
            beginAppearanceTransition(false, animated: false)
        }
        willMoveToParentViewController(nil)
        view.removeFromSuperview()
        removeFromParentViewController()
        if callAppearanceMethods{
            endAppearanceTransition()
        }
    }
}

最佳答案

不是将每个按钮放在它自己的 View 中,而是需要创建一个包含 3 个按钮的 View ,然后将圆圈添加到 View 中。

关于swift - Swift iPad 中的侧面板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39376448/

有关swift - Swift iPad 中的侧面板的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  8. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  9. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

  10. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

随机推荐