Ali Alebrahim Ali Alebrahim - 5 months ago 22
Swift Question

Can anybody help me understand this code - iOS swift

This is the code of creating a custom segmented control i found over the Internet. I have a problem in understanding the last two functions, beginTrackingWithTouch and layoutSubviews. What are the purpose of these functions and what does their code do exactly

and finally, excuse this question. I'm still a beginner in iOS development and I'm just seeking help.

@IBDesignable class CustomSegmentedControl : UIControl {
private var labels = [UILabel]()
var items = ["Item 1","Item 2"] {
didSet {
setUpLabels()
}
}
var thumbView = UIView()
var selectedIndex : Int = 0 {
didSet {
displayNewSelectedIndex()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
//layer.cornerRadius = frame.height / 2
layer.borderColor = UIColor.blackColor().CGColor
layer.borderWidth = 2

backgroundColor = UIColor.clearColor()
setUpLabels()

insertSubview(thumbView, atIndex: 0)
}
func setUpLabels() {
for label in labels {
label.removeFromSuperview()
}
labels.removeAll(keepCapacity: true)
for index in 1...items.count {
let label = UILabel(frame: CGRectZero)
label.text = items[index-1]
label.textAlignment = .Center
label.textColor = UIColor.blackColor()
self.addSubview(label)
labels.append(label)
}
}
func displayNewSelectedIndex() {
let label = labels[selectedIndex]
self.thumbView.frame = label.frame
}
override func layoutSubviews() {
super.layoutSubviews()
var selectFrame = self.bounds
let newWidth = CGRectGetWidth(selectFrame) / CGFloat(items.count)
selectFrame.size.width = newWidth
thumbView.frame = selectFrame
thumbView.backgroundColor = UIColor.grayColor()
//thumbView.layer.cornerRadius = thumbView.frame.height / 2

let labelHeight = self.bounds.height
let labelWidth = self.bounds.width / CGFloat(labels.count)

for index in 0..<labels.count {
let label = labels[index]
let xPosition = CGFloat(index) * labelWidth
label.frame = CGRectMake(xPosition, 0, labelWidth, labelHeight)
}
}
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
let location = touch.locationInView(self)
var calculatedIndex : Int?

for (index, item) in labels.enumerate() {
if item.frame.contains(location) {
calculatedIndex = index
}

if calculatedIndex != nil {
selectedIndex = calculatedIndex!
sendActionsForControlEvents(.ValueChanged)
}
}
return false
}
}

Answer

I can explain begin tracking method, the other one is researchable i think so

 /*** beginTrackingWithTouch.. method to customize tracking. ***/
    // Parameters : touch : this returns the touch that occurred at a certain point in the view. withEvent, returns the UIEvent
    // associated with the touch. 
    // Return Type: Boolean.
    override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
            let location = touch.locationInView(self)   // This line returns the location of touch in the view. This location is in CGPoint.
            var calculatedIndex : Int? 

            for (index, item) in labels.enumerate() {  /// enumeration of an array gives you sequence type of integer and corresponding element type of the array.
                if item.frame.contains(location) {     /// so labels.enumerate returns the key value pairs like so : [(0, labelatIndex0), (1, labelatIndex1).. and so on.]
                    calculatedIndex = index            /// here index is the key, and item is the value (label.)
    // So for every label/index pair, you check whether the touch happened on the label by getting the frame of the label and checking if location is a part of the frame
    /// You equate the index to calculatedIndex  
              }
    // Now you if your calculated index is a valid number, you class, which extends UIControl, will call its method stating the change of value(sendActionsForControlEvents).
    // This in turn, will send a message to all the targets that have been registered using addTarget:action:forControlEvents: method.
                if calculatedIndex != nil {  
                    selectedIndex = calculatedIndex!
                    sendActionsForControlEvents(.ValueChanged)
                }
            }

            return false
        }