How to call subclass method from superclass with protocol conformance - ios

I'm creating swift UI component that will provide some functionality to the UIView and UICollectionViewCell. I want to make it easy to customise. (code here are of course a simplification) In code below I will do some hard work in layoutSubview (I have to override layoutSubviews for each custom class because in extension we can't override class methods) and then call the add method with default implementation that should be easy to change its behaviour if needed.
Problem is that creating SubClassView instance calls correctly CustomView.layoutSubviews but inside this method SomeProtocol.add from extension is called instead of SubClassView.add. I understand that this is by Swift design but how can achieve that developer of my component without overriding layoutSubviews that I prepared for him, overrides only that one add method prepared by me for customisation with default implementation that is ready for him if he is not interested in changing it.
protocol SomeProtocol: class {
var property: Int { get set }
func add() // Method that can be override for custom behaviour
}
extension SomeProtocol where Self: UIView {
// Default implementation
func add() {
property += Int(frame.width)
print("SomeProtocol.add [property: \(property)]")
}
}
class CustomView: UIView, SomeProtocol {
var property: Int = 1
override func layoutSubviews() {
super.layoutSubviews()
print("CustomView.layoutSubviews")
// Some specific high end code for CustomView
add()
}
}
class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
var property: Int = 2
override func layoutSubviews() {
super.layoutSubviews()
print("CustomCollectionViewCell.layoutSubviews")
// Some specific high end code for CustomCollectionViewCell
add()
}
}
class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
// This method will not be called
func add() {
property += 10
print("SubClassView.add [property: \(property)]")
}
}
let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
// prints:
// CustomView.layoutSubviews
// SomeProtocol.add [property: 101]

You can add indirect class between yours CustomView and SomeProtocol, lets name it CustomViewBase. This class should inherit UIView and implement SomeProtocol. Now make your class CustomView subclass of CustomViewBase and add to it implementation of add() method and call from it super.add().
Here is code:
protocol SomeProtocol: class {
var property: Int { get set }
func add() // Method that can be override for custom behaviour
}
extension SomeProtocol where Self: UIView {
// Default implementation
func add() {
property += Int(frame.width)
print("SomeProtocol.add [property: \(property)]")
}
}
class CustomViewBase: UIView, SomeProtocol {
var property: Int = 1
}
class CustomView: CustomViewBase {
func add() {
super.add()
}
override func layoutSubviews() {
super.layoutSubviews()
print("CustomView.layoutSubviews 2")
// Some specific high end code for CustomView
add()
}
}
class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
var property: Int = 2
override func layoutSubviews() {
super.layoutSubviews()
print("CustomCollectionViewCell.layoutSubviews")
// Some specific high end code for CustomCollectionViewCell
add()
}
}
class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
// This method will not be called
override func add() {
property += 10
print("SubClassView.add [property: \(property)]")
}
}
let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

First, let's move all the animation stuff into its own thing.
struct CustomAnimator {
var touchesBegan: (UIView) -> Void = { $0.backgroundColor = .red() }
var touchesEnded: (UIView) -> Void = { $0.backgroundColor = .clear() }
}
So this is how we want to animate things. When touches begin, we'll make them red. When touches end, we'll make them clear. This is just an example, and of course can be overridden; these are just default values. This may not be the most convenient data structure for you; you could come up with lots of other ways to store this. But the key is that it's just a struct that animates the thing it is passed. It's not a view itself; there's no subclassing. It's just a value.
So, how can we use this? In cases where it's convenient to subclass, we can use it this way:
class CustomDownButton: UIButton {
var customAnimator: CustomAnimator?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
customAnimator?.touchesBegan(self)
super.touchesBegan(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
customAnimator?.touchesEnded(self)
super.touchesEnded(touches, with: event)
}
}
So if you feel like subclassing your view, you can just assign a CustomAnimator onto it and it'll do the thing. But that's not really the interesting case, IMO. The interesting case is when it's not convenient to subclass. We want to attach this to a normal button.
UIKit already gives us a tool for attaching touch behavior to random views without subclassing them. It's called a gesture recognizer. So let's build one that does our animations:
class CustomAnimatorGestureRecognizer: UIGestureRecognizer {
let customAnimator: CustomAnimator
init(customAnimator: CustomAnimator) {
self.customAnimator = customAnimator
super.init(target: nil, action: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if let view = view {
customAnimator.touchesBegan(view)
}
super.touchesBegan(touches, with: event)
}
override func reset() {
// I'm a bit surprised this isn't in the default impl.
if self.state == .possible {
self.state = .failed
}
super.reset()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
if let view = view {
customAnimator.touchesEnded(view)
}
self.reset()
super.touchesEnded(touches, with: event)
}
}
It should be pretty obvious how this works. It just calls the animator to modify our view any time touches start or stop. There's a little bit of bookkeeping needed to properly handle the state machine, but it's very straightforward.
There is no step three. Setting this up is trivial:
override func viewDidLoad() {
super.viewDidLoad()
customButton.customAnimator = CustomAnimator()
normalButton.addGestureRecognizer(CustomAnimatorGestureRecognizer(customAnimator: CustomAnimator()))
}
Full project on GitHub.

I play with this and I came up with 2 different approaches.
(github with playground -> https://github.com/nonameplum/Swift-POP-with-Inheritance-problem)
Composition
struct BaseFunctionality<T: UIView where T: SomeProtocol> {
var add: (T) -> Void
init() {
add = { (view) in
view.property += Int(view.frame.width)
print("BaseFunctionality [property: \(view.property)]")
}
}
}
protocol SomeProtocol: class {
var property: Int { get set }
}
class CustomView: UIView, SomeProtocol {
var property: Int = 1
var baseFunc = BaseFunctionality<CustomView>()
override func layoutSubviews() {
super.layoutSubviews()
print("CustomView.layoutSubviews")
// Some specific high end code for CustomView
baseFunc.add(self)
}
}
class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
var property: Int = 2
var baseFunc = BaseFunctionality<CustomCollectionViewCell>()
override func layoutSubviews() {
super.layoutSubviews()
print("CustomCollectionViewCell.layoutSubviews")
// Some specific high end code for CustomCollectionViewCell
baseFunc.add(self)
}
}
class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
override init(frame: CGRect) {
super.init(frame: frame)
self.baseFunc.add = { (view) in
view.property -= Int(view.frame.width)
print("SubClassView [property: \(view.property)]")
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
Kind of delegates
protocol Functionality: class {
func add()
}
// Default implementation
extension SomeProtocol where Self: UIView {
func add() {
property = -5
print("Functionality.add [property = \(property)]")
}
}
protocol SomeProtocol: class {
var property: Int { get set }
var delegate: Functionality? { get set }
}
class CustomView: UIView, SomeProtocol {
var property: Int = 1
weak var delegate: Functionality?
override func layoutSubviews() {
super.layoutSubviews()
print("CustomView.layoutSubviews")
// Some specific high end code for CustomView
if let delegate = delegate {
delegate.add()
} else {
self.add()
}
}
}
class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
var property: Int = 2
weak var delegate: Functionality?
override func layoutSubviews() {
super.layoutSubviews()
print("CustomCollectionViewCell.layoutSubviews")
// Some specific high end code for CustomCollectionViewCell
if let delegate = delegate {
delegate.add()
} else {
self.add()
}
}
}
class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
override init(frame: CGRect) {
super.init(frame: frame)
self.delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SubClassView: Functionality {
func add() {
property = 5
print("SubClassView.add [property = \(property)]")
}
func doNothing() { print("SubClassView.doNothing [\(property)]") }
}
let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

Related

Protocol/Delegate function doesn't trigger (Swift) [duplicate]

I have an IBOutlet-connected UIView object (innerView) sitting over view controller (UIViewController)'s view. I want to let the user rotate innerView with their finger. So I have a subclass (TouchView) of UIView set to innerView's class. As the user rotates the view object, I want the view controller to receive a value from TouchView. And what I have is the following.
// UIView
protocol TouchDelegate: class {
func degreeChanged(degree: Double)
}
class TouchView: UIView {
weak var delegate: TouchDelegate? = nil
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let aTouch: AnyObject = touches.first! as UITouch
let newLoc = aTouch.location(in: self.superview)
...
...
let degree = { (radians: Double) -> Double in
return radians * 180 / M_PI
}
self.delegate?.degreeChanged(degree: degree)
}
}
// UIViewController
class ViewController: UIViewController, TouchDelegate {
#IBOutlet weak var innerView: UIView!
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
let touchView = TouchView()
touchView.delegate = self
}
// MARK: - From TouchView
func degreeChanged(degree: Double) {
print(degree) // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< no call
}
}
Well, the view controller never receives a delegate call from TouchView. What am I doing wrong? Thank you for your help.
I see 2 problems
You created a new instance of touchView and did not add it into your view controller's views
Your innerView is not an instance of touchView and its delegate is not set
My approach to your situation would look something like this:
// UIViewController
class ViewController: UIViewController, TouchDelegate {
#IBOutlet weak var innerView: TouchView!
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
innerView.delegate = self
}
// MARK: - From TouchView
func degreeChanged(degree: Double) {
print(degree) // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< no call
}
}

touch up inside (#IBAction) does not work with UIButton subclass

I created a subclass of UIButton:
import UIKit
#IBDesignable
class CheckboxButton: UIButton {
#IBInspectable var checkboxBackgroundColor: UIColor = Project.Color.baseGray
#IBInspectable var textColor: UIColor = Project.Color.mainDarkText
#IBInspectable var checkboxHighlightedBackgroundColor: UIColor = Project.Color.main
#IBInspectable var highlightedTextColor: UIColor = Project.Color.mainBrightText
// MARK: - Properties
var isChecked: Bool = false {
didSet {
changeState()
}
}
// MARK: - Overrides
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
if isChecked {
isChecked = false
} else {
isChecked = true
}
return false
}
override func prepareForInterfaceBuilder() {
changeState()
}
// MARK: - #IBActions
// MARK: - Functions
private func setupView() {
isUserInteractionEnabled = true
changeState()
}
private func changeState() {
if isChecked {
backgroundColor = checkboxHighlightedBackgroundColor
self.setTitleColor(highlightedTextColor, for: .normal)
} else {
backgroundColor = checkboxBackgroundColor
self.setTitleColor(textColor, for: .normal)
}
}
}
Now I added a button inside the storyboard and gave it the class CheckboxButton. Everything works. Then I added an IBAction like this:
#IBAction func pointBtnTapped(_ sender: CheckboxButton) {
print("tapped")
selectButton(withNumber: sender.tag)
}
But this doesn't work (nothing prints out). It works with a normal button, but not if the button is the subclass CheckboxButton. Do you have any ideas?
Edit: screenshots
https://www.dropbox.com/s/ewicc349ag9l6y2/Screenshot%202016-10-06%2023.41.39.png?dl=0
https://www.dropbox.com/s/vxevzscueproivc/Screenshot%202016-10-06%2023.42.33.png?dl=0
(couldn't embed them here. Don't know why)
Thank you!
You broke UIButton by overriding beginTracking() and always returning false. This brainwashes the button into thinking it's never being clicked.
What was your intent there? In any case, return true there and your code will fire the #IBAction.
EDIT: You're overthinking the issue by using a low-level method meant for highly customized behavior such as non-rectangular buttons. You just need:
How to use UIButton as Toggle Button?

Extension UIButton only when conforming to a protocol

I'm trying to create several extensions for UIButton so I can add some functionality easily just by adding a protocol to a custom button. It'd be a lot easier if I didn't have to override some methods in UIButton so I'm thinking I'll have to make an extension for UIButton itself.
For example, I have several protocols my custom buttons can conform to:
protocol CustomLayer { }
protocol ButtonSound { }
So far I only managed to create an extension for UIButton without any constraints (these are simplified versions):
// Only when the button conforms to protocol CustomLayer
extension UIButton {
override public class func layerClass() -> AnyClass { return CAShapeLayer.self }
}
// Only when the button conforms to protocol ButtonSound
extension UIButton {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
AudioServicesPlaySystemSound(1104)
}
}
I've read posts I can make an extension for the protocol itself with a where clause for the class UIButton:
extension CustomLayer where Self: UIButton { }
But then I can't override any methods of UIButton itself.
Other suggestions like subclassing works, but certain buttons can't have the same functionality:
class A: CustomLayer { } // Can't have ButtonSound, single subclass works
class B: ButtonSound { } // Can't have CustomLayer, single subclass works
class C: CustomLayer, ButtonSound { } // Error: multiple inheritance from classes
import Foundation
protocol BackButtonDelegate {
func BackButtonAction(sender:UIButton)
}
class NavigationBack: UIButton
{
var delegate : BackButtonDelegate!
override func drawRect(rect: CGRect) {
self.frame = CGRectMake(0, 0, 60, 60)
self.setTitle("Back", forState: .Normal)
self.titleLabel?.font = UIFont(name: "Helvetica",size: 12)
self.setImage(UIImage(named: "back-arrow"), forState: .Normal)
self.addTarget(self, action: #selector(NavigationBack.click(_:)), forControlEvents: UIControlEvents.TouchUpInside)
}
func click(sender:UIButton)
{
if delegate != nil
{
delegate!.BackButtonAction(sender)
}
}
}

Swift 2.2 #selector in protocol extension compiler error

I've got a protocol extension it used to work perfectly before swift 2.2.
Now I have a warning that tells me to use the new #selector, but if I add it
no method declared with Objective-C Selector.
I tried to reproduce the issue in this few lines of code, that can be easily copy and paste also into playground
protocol Tappable {
func addTapGestureRecognizer()
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer() {
let gesture = UITapGestureRecognizer(target: self, action:#selector(Tappable.tapGestureDetected(_:)))
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
There is also a suggestion to append to that method in the protocol #objc, but if I do it asks me also to add it to the class that implements it, but once I add the class doesn't conform to the protocol anymore, because it doesn't seems to see the implementation in the protocol extension.
How can I implement this correctly?
I had a similar problem. here is what I did.
Marked the protocol as #objc.
Marked any methods I extended with a default behavior as optional.
Then used Self. in the #selector.
#objc public protocol UpdatableUserInterfaceType {
optional func startUpdateUITimer()
optional var updateInterval: NSTimeInterval { get }
func updateUI(notif: NSTimer)
}
public extension UpdatableUserInterfaceType where Self: ViewController {
var updateUITimer: NSTimer {
return NSTimer.scheduledTimerWithTimeInterval(updateInterval, target: self, selector: #selector(Self.updateUI(_:)), userInfo: nil, repeats: true)
}
func startUpdateUITimer() {
print(updateUITimer)
}
var updateInterval: NSTimeInterval {
return 60.0
}
}
You can create a property which is a Selector... Example:
protocol Tappable {
var selector: Selector { get }
func addTapGestureRecognizer()
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer() {
let gesture = UITapGestureRecognizer(target: self, action: selector)
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
var selector = #selector(TapView.tapGestureDetected(_:))
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
The error stops to show and it is not more necessary to set your protocol and class with the #objc decorator.
This solution is not the most elegant, but looks ok until now.
This answer is quite similar to Bruno Hecktheuers, but instead of having everyone that wants to conform to the "Tappable" protocol implement the variable "selector", we choose to pass it as a parameter to the addTapGestureRecognizer function:
protocol Tappable {
func addTapGestureRecognizer(selector selector: Selector)
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer(selector selector: Selector)
let gesture = UITapGestureRecognizer(target: self, action: selector)
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
and then just pass the selector wherever it is used:
addTapGestureRecognizer(selector: #selector(self.tapGestureDetected(_:)))
This way we avoid having the ones implementing this protocol having to implement the selector variable and we also avoid having to mark everyone using this protocol with "#objc". Feels like this approach is less bloated.
Here is a working example using Swift 3. It uses a standard Swift protocol without the need for any #objc decorations and a private extension to define the callback function.
protocol PlayButtonPlayable {
// be sure to call addPlayButtonRecognizer from viewDidLoad or later in the display cycle
func addPlayButtonRecognizer()
func handlePlayButton(_ sender: UITapGestureRecognizer)
}
fileprivate extension UIViewController {
#objc func _handlePlayButton(_ sender: UITapGestureRecognizer) {
if let playable = self as? PlayButtonPlayable {
playable.handlePlayButton(sender)
}
}
}
fileprivate extension Selector {
static let playTapped =
#selector(UIViewController._handlePlayButton(_:))
}
extension PlayButtonPlayable where Self: UIViewController {
func addPlayButtonRecognizer() {
let playButtonRecognizer = UITapGestureRecognizer(target: self, action: .playTapped)
playButtonRecognizer.allowedPressTypes = [ NSNumber(value: UIPressType.playPause.rawValue as Int) ]
view.addGestureRecognizer(playButtonRecognizer)
}
}
I happened to see this in the side bar, I recently had this same issue.. Unfortunately, due to Objective-C runtime limitations you cannot use #objc on protocol extensions, I believe this issue was closed early this year.
The issue arises because the extension is added after the conformance of the protocol, therefor there is no way to guarantee that conformance to the protocol is met. That said, it is possible to call a method as a selector from anything that subclasses NSObject and conforms to the protocol. This is most often done with delegation.
This implies you could create an empty wrapper subclass that conforms to the protocol and use the wrapper to call its methods from the protocol that are defined in the wrapper, any other undefined methods from the protocol can be passed to the delegate. There are other similar solutions that use a private extension of a concrete class such as UIViewController and define a method that calls the protocol method but these are also tied to a particular class and not a default implementation of a particular class that happens to conform to the protocol.
Realize that you are trying to implement a default implementation of a protocol function that uses another of it's own protocol functions to define a value for it's own implementation. whew!
Protocol:
public protocol CustomViewDelegate {
func update()
func nonDelegatedMethod()
}
View:
Use a delegate, and define a wrapper method to safely unwrap the delegate’s method.
class CustomView: UIView {
let updateButton: UIButton = {
let button = UIButton(frame: CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 150, height: 50)))
button.backgroundColor = UIColor.lightGray
button.addTarget(self, action: #selector(doDelegateMethod), for: .touchUpInside)
return button
}()
var delegate:CustomViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(updateButton)
}
#objc func doDelegateMethod() {
if delegate != nil {
delegate!.update()
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
ViewController:
Conform the View Controller to the view’s delegate: and implement the protocol’s method.
class ViewController: UIViewController, CustomViewDelegate {
let customView = CustomView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
customView.backgroundColor = UIColor.red
customView.delegate = self //if delegate is not set, the app will not crash
self.view.addSubview(customView)
}
// Protocol -> UIView Button Action -> View Controller's Method
func update() {
print("Delegating work from View that Conforms to CustomViewDelegate to View Controller")
}
//Protocol > View Controller's Required Implementation
func nonDelegatedMethod() {
//Do something else
}
}
Note that the view controller only had to conform to the delegate and did not set the selector of some property of the view, this separates the view (and it's protocol) from view controller.
You already have a UIView named TapView that inherits from UIView and Tappable so your implementation could be:
Protocol:
protocol TappableViewDelegate {
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
TappableView:
class TappableView: UIView {
var delegate:TappableViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
let gesture = UITapGestureRecognizer(target: self, action: #selector(doDelegateMethod(gesture:)))
addGestureRecognizer(gesture)
}
#objc func doDelegateMethod(gesture:UITapGestureRecognizer) {
if delegate != nil {
delegate!.tapGestureDetected(gesture: gesture)
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
ViewController:
class ViewController: UIViewController, TappableViewDelegate {
let tapView = TappableView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
tapView.backgroundColor = UIColor.red
tapView.delegate = self
self.view.addSubview(tapView)
}
func tapGestureDetected(gesture: UITapGestureRecognizer) {
print("User did tap")
}
}

Access variable in ViewController from a different class in Swift

I have a slider sliderLineSize and a variable lineSize in a ViewController. The UISlider sliderLineSize changes lineSize. However, lineSize actually used in the drawRect section of the viewLine class which attaches to a UIView.
Question:
How do I pass or make accessible the variable lineSize which is set in the ViewController to the class viewLine where it is used in drawRect?
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()
}
}
There are two main ways to do this.
Option 1:
Give your ViewLine class its own lineSize property:
class ViewLine: UIView {
var lineSize = 1
}
Give ViewController a reference to ViewLine, and use a property observer to update the property inside viewLine whenever it changes in ViewController:
class ViewController: UIViewController {
// v~~~ be sure to connect this outlet in interface builder
#IBOutlet weak var viewLine: ViewLine!
var lineSize = 1 {
didSet {
viewLine.lineSize = lineSize
}
}
}
Now your ViewLine class will have its own lineSize property that can be accessed from within its drawRect method directly.
Option 2:
Give your ViewLine class a reference to ViewController:
class ViewLine: UIView {
// v~~~ be sure to connect this outlet in interface builder
#IBOutlet weak var controller: ViewController!
}
Now, in your drawRect method, replace path.lineWidth = lineSize with path.lineWidth = controller.lineSize.
Basically, one of your classes needs a reference to the other in order for them to be able to communicate.
You should make Singleton Model class. A singleton class can be accessed from anywhere. Here is how you should create a singleton class in swift.
class ApplicationModel {
class var sharedInstance: ApplicationModel {
get {
struct Static {
static var instance: ApplicationModel? = nil
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token, {
Static.instance = ApplicationModel()
})
return Static.instance!
}
}
var lineSize = 1
}
Inside ViewController
override func viewDidLoad() {
super.viewDidLoad()
//Instantiate ApplicationModel
//GET
let lineSize = ApplicationModel.sharedInstance.lineSize
//SET
ApplicationModel.sharedInstance.lineSize = 5
}
Inside viewLine
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//Access Application Model
//GET
let lineSize = ApplicationModel.sharedInstance.lineSize
//SET
ApplicationModel.sharedInstance.lineSize = 5
}
Hope this helps!

Resources