从 Swift 中的不同类访问 ViewController 中的对象

Posted

技术标签:

【中文标题】从 Swift 中的不同类访问 ViewController 中的对象【英文标题】:Access objects in ViewController from a different class in Swift 【发布时间】:2016-02-10 16:01:21 【问题描述】:

我有一个 ViewController,其中有一个名为 myView 的 UIView 和一个名为 myImageView 的 UIImageView。在下面的代码中,我有一个名为 viewLine 的类,它附加到名为 myView 的 UIView。

我遇到的问题是当touchesEnded 被调用时,我想在ViewController 中更改myImageView 的alpha。当我尝试这个时,myImageView 的 alpha 不会发生任何变化。

(或者,当我尝试通过将 viewLine 类移动到主 ViewController 来实现此目的时,出现以下错误 - override func drawRect(rect: CGRect) - 方法不会覆盖其超类中的任何方法self.setNeedsDisplay() - “ViewController”类型的值没有成员“setNeedsDisplay”。)

问题:

1 - 如何修改名为 viewLine 的类中的代码以访问 ViewController 故事板上的其他 UI 对象或函数,例如 myImageView?即如何在调用touchesEnded 后从名为viewLine 的类中更改myImageView 的alpha?

2 - 我在 ViewController 中有一个滑块 sliderLineSize,在 ViewController 中有一个变量 lineSize。 UISlider sliderLineSize 更改 lineSize。但是,lineSize 用于viewLine 类的drawRect 部分。 如何在类的 ViewController 中传递或访问设置的变量?

(3 - 如何将viewLine 类合并到主 ViewController 中?)

import UIKit

class ViewController: UIViewController 

    @IBOutlet weak var myView: UIView!
    @IBOutlet weak var myImageView: UIImageView!

    var lineSize: Int = 1

    override func viewDidLoad() 
        super.viewDidLoad()
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
    

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) 
        myImageView.alpha = 0.5
    

    @IBAction func sliderLineSize(sender: UISlider) 
        lineSize = Int(sender.value)
    



class viewLine: UIView 

    let path=UIBezierPath()
    var incrementalImage:UIImage?
    var previousPoint:CGPoint = CGPoint.zero
    var strokeColor:UIColor?

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
    

    override func drawRect(rect: CGRect) 
            incrementalImage?.drawInRect(rect)
            path.lineWidth = lineSize
            path.stroke()
    

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
        let touch: AnyObject? = touches.first
        let currentPoint = touch!.locationInView(self)
        path.moveToPoint(currentPoint)
        previousPoint=currentPoint
        self.setNeedsDisplay()
    

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) 
        let touch: AnyObject? = touches.first
        let currentPoint = touch!.locationInView(self)
        let midPoint = self.midPoint(previousPoint, p1: currentPoint)
        path.addQuadCurveToPoint(midPoint,controlPoint: previousPoint)
        previousPoint=currentPoint
        path.moveToPoint(midPoint)
        self.setNeedsDisplay()
    

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) 
        self.drawBitmap()
        self.setNeedsDisplay()
        path.removeAllPoints()
    

    func midPoint(p0:CGPoint,p1:CGPoint)->CGPoint 
        let x=(p0.x+p1.x)/2
        let y=(p0.y+p1.y)/2
        return CGPoint(x: x, y: y)
    

    func drawBitmap() 
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 1)
        strokeColor?.setStroke()
        if((incrementalImage) == nil)
            let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds)
            UIColor.whiteColor().setFill()
            rectPath.fill()
        
        incrementalImage?.drawAtPoint(CGPointZero)
        path.stroke()
        incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    


【问题讨论】:

【参考方案1】:

我查找了如何从其子视图中引用 ViewController,我发现最好的选择是 *** 的答案,上面写着“按照 MVC 模式,ViewController 知道它的 View,但 View 不应该知道 ViewController。相反,您应该声明委托协议”(来源:Swift - How to pass a view controller's reference to a sub UIView class?)

所以,我认为解决方案是编写自定义委托协议并让 ViewController 遵守它。上述答案中的代码编写委托协议有点过时,但Pure Swift class conforming to protocol with static method - issue with upcasting 中问题的开头显示了如何正确编写在我的 Xcode 7.2 中工作的协议。

我的故事板有一个这样的图表:

查看 我的观点(链接到您的代码) 我的图像视图(背景是我选择的静态图像)

我将类 viewLine 重命名为 ViewLineUIView,因为我认为这将是底层超类的更具描述性的名称。代码如下。

代码:

import UIKit

protocol ViewLineUIViewDelegate: class 
    func onTouchesEnded(viewLine: ViewLineUIView)


class ViewController: UIViewController, ViewLineUIViewDelegate 

    @IBOutlet weak var myView: UIView!
    @IBOutlet weak var myImageView: UIImageView!
    @IBOutlet weak var sliderValue: UISlider!

    var viewLineDelegate: ViewLineUIView!

    override func viewDidLoad() 
        super.viewDidLoad()
        viewLineDelegate = myView as? ViewLineUIView
        viewLineDelegate!.delegate = self

        //update lineWidth to initial value of slider
        updateLineWidth()
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
    

    func onTouchesEnded(viewLine: ViewLineUIView) 
        myImageView!.alpha = 0.5
    

    //Updating lineWidth!
    @IBAction func onSliderChange(sender: UISlider) 
        updateLineWidth()

    

    func updateLineWidth() 
        let drawView = myView as! ViewLineUIView
        //you can change this "10" value to your max lineWidth
        drawView.path.lineWidth = CGFloat(sliderValue.value*10)
        print("New value is: " + String(drawView.path.lineWidth))
    




class ViewLineUIView: UIView 

    weak var delegate: ViewLineUIViewDelegate?

    let path=UIBezierPath()
    var incrementalImage:UIImage?
    var previousPoint:CGPoint = CGPoint.zero
    var strokeColor:UIColor?

    required init?(coder aDecoder: NSCoder) 
        //Initialize lineWidth so that compiler doesn't complain
        super.init(coder: aDecoder)
    

    override func drawRect(rect: CGRect) 
        incrementalImage?.drawInRect(rect)
        path.stroke()
    

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
        let touch: AnyObject? = touches.first
        let currentPoint = touch!.locationInView(self)
        path.moveToPoint(currentPoint)
        previousPoint=currentPoint
        self.setNeedsDisplay()
    

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) 
        let touch: AnyObject? = touches.first
        let currentPoint = touch!.locationInView(self)
        let midPoint = self.midPoint(previousPoint, p1: currentPoint)
        path.addQuadCurveToPoint(midPoint,controlPoint: previousPoint)
        previousPoint=currentPoint
        path.moveToPoint(midPoint)
        self.setNeedsDisplay()
    

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) 
        self.drawBitmap()
        self.setNeedsDisplay()
        path.removeAllPoints()

        delegate?.onTouchesEnded(self)
    

    func midPoint(p0:CGPoint,p1:CGPoint)->CGPoint 
        let x=(p0.x+p1.x)/2
        let y=(p0.y+p1.y)/2
        return CGPoint(x: x, y: y)
    

    func drawBitmap() 
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 1)
        strokeColor?.setStroke()
        if((incrementalImage) == nil)
            let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds)
            UIColor.whiteColor().setFill()
            rectPath.fill()
        
        incrementalImage?.drawAtPoint(CGPointZero)
        path.stroke()
        incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    


如您所见,编辑路径的 lineWidth 很简单,您不需要创建另一个委托协议。 ViewController 可以直接访问其子视图。您应该只知道需要演员表。

【讨论】:

谢谢@Ataias Reis。这似乎工作得恰到好处。我在最初的问题中遗漏了一些关于如何处理变量的问题,我已经更新了:我在 ViewController 中有一个滑块 sliderLineSize,在 ViewController 中有一个变量 lineSize。 UISlider sliderLineSize 更改 lineSize。但是,lineSize 用于viewLine 类的drawRect 部分。如何在类的 ViewController 中传递或访问设置的变量? 我改变了答案。希望对您有所帮助! 谢谢。这帮助我重回正轨。

以上是关于从 Swift 中的不同类访问 ViewController 中的对象的主要内容,如果未能解决你的问题,请参考以下文章

从不同名称空间中的不同类访问变量

如何从已在其中创建的不同类对象中访问类对象的成员函数?

如何跨具有TestNG测试的不同类访问dependsOnMethod或dependsOnGroup?

从 Python 中的不同类打印?

是否可以在 Symfony 4 中的不同类上使用函数?

如何根据 API 级别从 Swift 中的两个不同类派生一个类?