将类设置为 UITapGestureRecognizer 的目标而不是“自我”时应用程序崩溃

Posted

技术标签:

【中文标题】将类设置为 UITapGestureRecognizer 的目标而不是“自我”时应用程序崩溃【英文标题】:App crashes when setting a class to UITapGestureRecognizer's target instead of 'self' 【发布时间】:2015-02-03 07:56:07 【问题描述】:

当我将“self”设置为 UITapGestureRecognizer 实例的目标时,下面的代码有效,但是当我将其设置为“SkinViewTransitionHelper”实例时,应用程序崩溃并且 Xcode 没有显示任何有用的信息,实际上什么也没有,什么时候应用程序崩溃控件(我的意思是绿色指示器)转到 AppDelegate。此外,堆栈跟踪是空的,仅显示一些与崩溃无关的线程。我确实搜索了很多并重新阅读了 UIGestureRecognizer 的文档,但没有帮助。我究竟做错了什么?我在 Xcode 版本 6.2 (6C107a) 中编写应用程序。另外,我正在 ios 模拟器版本 8.2 (553.8) 中测试该应用程序。我也检查了UITapGestureRecognizer calling another class

/**
     Manages the view that contains logo and button.
     */
    class LaunchView: UIView 
        // MARK: Instance variables
        /** Logo image view */
        private var logoImageView:    UIImageView!
        /** x Button image view */
        private var xButtonImageView: UIImageView!

        // MARK: Designated Initializer

        /**
           Initialize LaunchView view with logo and button.

           :param: frame        Frame of this view
           :param: logoImage    Logo Image used at the top of the view
           :param: xButtonImage Button Image used at the bottom of the view
        */
        init(frame: CGRect, logoImage: UIImage, xButtonImage: UIImage)
            super.init(frame: frame)

            // Initialize the logoImageView
            logoImageView = UIImageView(frame: frame)
            // Set the image of logoImageView
            logoImageView.image = logoImage

            // Initialize the capriceButtonImage
            // Note: It's kinda hard coding the (x,y) values. Look for a generic way.
            xButtonImageView = UIImageView(frame: CGRect(x: frame.size.width / 4 + 20, y: frame.size.height - 200, width: xButtonImage.size.width, height: xButtonImage.size.height))
            // Set the image of xButtonImageView
            xButtonImageView.image = xButtonImage

            // SingleTap recognizer for xButtonImageView
            // Note: Find a generic solution instead of writing method name in "". It's kinda horrible; Maybe I forget to write the name of function correctly.
            // Note: Still not possible in Swift. reflect() helps if we want information about properties.
            // Note: https://github.com/ksm/SwiftInFlux#reflection
            // FIXME: App crashes using SkinViewTransitionHelper() as target
            let singleTapGestureRecognizer: UITapGestureRecognizer = 
                   UITapGestureRecognizer(target: SkinViewTransitionHelper(), action: "handleGesture:")

            // works like a charm if I remove the comment and comment above line
            // let singleTapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleGesture:")

            // Set the nunmber of taps required
            singleTapGestureRecognizer.numberOfTapsRequired = 1
            singleTapGestureRecognizer.numberOfTouchesRequired = 1

            // Add tap gesture to xButtonImageView

            xButtonImageView.addGestureRecognizer(singleTapGestureRecognizer)

            xButtonImageView.userInteractionEnabled = true

            // Add logoImageView to the view
            self.addSubview(logoImageView)
            // Add xButtonImageView to the view
            self.addSubview(xButtonImageView)
        

        required init(coder aDecoder: NSCoder) 
            fatalError("init(coder:) has not been implemented")
        

        // func handleGesture(gestureRecongnizer: UIGestureRecognizer) 
            // if .Ended == gestureRecongnizer.state 
                // T0D0: Notify LaunchViewController of gesture
            // 
        // 
    

    /**
    Handle gesture recognized by a UIView instance
    */
    class SkinViewTransitionHelper 
        // MARK: Gesture Handler

        /**
        Handle gesture recognized by a UIView instance
        */
        func handleGesture(gestureRecongnizer: UIGestureRecognizer) 
            println(__FUNCTION__)

            if .Ended == gestureRecongnizer.state 
                // TODO: Load SkinViewController
            
        
    

import UIKit

class LaunchViewViewController: UIViewController 

    override func viewDidLoad() 
        println(self)
        println(__FUNCTION__)

       super.viewDidLoad()
    

    override func loadView() 
        println(self)
        println(__FUNCTION__)

        // Set LaunchView as the view of LaunchViewViewController
        self.view = LaunchView(frame: UIScreen.mainScreen().bounds, logoImage: ImagesCatalog.Logo, xButtonImage: ImagesCatalog.xButton)
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    

【问题讨论】:

【参考方案1】:

它崩溃是因为targetUITapGestureRecognizer 类的弱属性,所以SkinViewTransitionHelper 的实例不会被UITapGestureRecognizer 保留。

创建SKinViewTransitionHelper 类型的属性并在其上存储一个实例并将其设置为UITapGestureRecognizer 目标。

class LaunchView 

    var skinViewHelper : SKinViewTransitionHelper!

     init(frame: CGRect, logoImage: UIImage, xButtonImage: UIImage)

         //Your code
         self.skinViewHelper = SKinViewTransitionHelper()
         let singleTapGestureRecognizer: UITapGestureRecognizer = 
                   UITapGestureRecognizer(target: self.skinViewHelper, action: "handleGesture:")
     

同时用@objc将你想要引用的函数标记为选择器

class SkinViewTransitionHelper 

    @objc func handleGesture(gestureRecongnizer: UIGestureRecognizer) 
        println(__FUNCTION__)

        if .Ended == gestureRecongnizer.state 
            // TODO: Load SkinViewController
        
    

【讨论】:

【参考方案2】:

如果您实例化 SkinViewTransitionHelper 并将其值存储到变量中(可能需要是类属性)然后将其分配为目标会发生什么?

我认为您的 SkinViewTransitionHelper 实例可能在“目标”分配后被释放,因为在任何地方都没有硬引用它。

【讨论】:

以上是关于将类设置为 UITapGestureRecognizer 的目标而不是“自我”时应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何将类属性设置为 Symfony2 表单输入

使用 *ngFor 时如何仅将类设置为特定元素?

将类序列化为单个无属性元素,其内容设置为一个属性

如何将类属性设置为类方法返回类型?

如何将类设置为 django 模型表单集中的列

有没有办法将类中的枚举属性设置为所有可用的枚举?