Tuesday, December 18, 2018

How to auto resize a button's image according to the button's size in Swift 3

I'm developing a pet project called MyCalculator using Swift 3.


When the screen is rotated from portrait to landscape, more buttons appears then each button become smaller to fit the screen and does its image.


Without any additional codes, the image does not get smaller if its size is smaller than the button's in both portrait and landscape modes. This does not look good. What i want is, for example, the space between the image's top and the button's top should always equal 30% of the button's height and the image should get smaller or bigger according to the space.

I created a custom UIButton as following:

       
import Foundation
import UIKit

public class CustomButton : UIButton {
    private let offset = CGFloat(2.0) // 2 point

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        self.imageView?.contentMode = .scaleAspectFit
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
        
        let edge = bounds.height / 3 - offset
        self.imageEdgeInsets = UIEdgeInsets(top: edge, left: 0, bottom: edge, right: 0)
    }   
}
       

The following line means the button's image scales to fit the available space by maintaining the aspect ratio. In other words, if the image's height changes, its width changes too proportional to the height.


self.imageView?.contentMode = .scaleAspectFit 
       

The imageEdgeInsets property is used to set the spaces around the image on the button. A button can have both image and title (text) at the same time. The titleEdgeInsets property is used to define the spaces around the title. There is another related property called contentEdgeInsets, which is used to set the spaces around both image and title together. I only specified the values of the top and the bottom because I need the top and bottom spaces to be equal.

The layoutSubviews() method is called when bounds or frame property changes, according to https://forums.developer.apple.com/message/317152?et=watches.email.thread#317152.


Unreliable Solution


It works for my case but it's not guaranteed since the bounds property is a computed property, not a stored property so it might have its own instance variable, which might be set internally without going through the bounds property then didSet and willSet would not be called.

import Foundation
import UIKit

public class CustomButton : UIButton {
    private let offset = CGFloat(2.0) // 2 point
    
    public override var bounds: CGRect {
        didSet {
            self.boundDidChange()
        }
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        self.imageView?.contentMode = .scaleAspectFit
    }
    
    public func boundDidChange() {
        let edge = bounds.height / 3 - offset
        self.imageEdgeInsets = UIEdgeInsets(top: edge, left: 0, bottom: edge, right: 0)
    }
}
       



References
https://developer.apple.com/documentation/uikit/uiview/contentmode/scaleaspectfit
https://developer.apple.com/documentation/uikit/uibutton/1624034-imageedgeinsets

Friday, December 14, 2018

Displaying text and images together on UILabel in Swift 3

I'm developing a pet project called MyCalculator. I need to display the current operations the user is entering. I display the numbers and Math operators on a label. I inserted an image of  the square root of x operator in the label when the user touches the operator button.


The code snippet below show how to insert text and inline image in a UILabel.



    private var operatorImages: [Int:UIImage] = [
        103:UIImage(named: "squareRootX")!,
        116:UIImage(named: "cubeRootX")!,
        206:UIImage(named: "yRootX")!,
    ]
    private var operatorSymbolsCache : [Int:NSTextAttachment] = [:]
    
    private func getOperatorSymbol(_ tag: Int) -> NSTextAttachment {
        var result = operatorSymbolsCache[tag]
        if result == nil {
            result = NSTextAttachment()
            result?.image = operatorImages[tag]!
            result?.bounds = CGRect(x: 0.0, y: 0.0, width: Double(operatorImages[tag]!.size.width), height: Double(operatorImages[tag]!.size.height))
            operatorSymbolsCache[tag] = result
        }
        return result!
    }

    ...

    var myText = NSMutableAttributedString()
    myText.append(NSAttributedString(string: operand))
    myText.append(NSAttributedString(attachment: getOperatorSymbol(currentOperations[i].1.tag)))

    currentOperationsLabel.attributedText = myText
 
       
 

The problem is the operator image is not vertically center like in the image below.


I fixed it by updating the line:



result?.bounds = CGRect(x: 0.0, y: 0, width: Double(operatorImages[tag]!.size.width), height: Double(operatorImages[tag]!.size.height))
 
       
 

to:



result?.bounds = CGRect(x: 0.0, y: Double((displaySubSection.font.capHeight - operatorImages[tag]!.size.height) / 2), width: Double(operatorImages[tag]!.size.width), height: Double(operatorImages[tag]!.size.height))
 
       

I changed the y parameter. The graph below show what capHeight property is.


The properties capHeight, size.height, and size.width are in point. If the image dimension is 30x30 pixels, the size.height property would be 15 (points) and size.width property would be 15 (points) when the app is running on high resolution devices such as iPhone 5s and iPhone 6 because one point contains 2 pixels. To get the property's value in pixels, multiply it by the scale property of the UIImage class.



References
https://developer.apple.com/documentation/uikit/uiimage/1624105-size
https://stackoverflow.com/questions/26105803/center-nstextattachment-image-next-to-single-line-uilabel





Thursday, December 13, 2018

How to cut out an image in Photoshop

1. Open an image in Photoshop
2. Hold down the left mouse button on the Lasso Tool (L) icon from the Tools panel and select Polygonal Lasso Tool (L) from the popup.
3. Trace around the image you want to cut out. When you're done, the selection marquee shows up to around the image.


4. Press Ctrl + J (or command + J on Mac) to cut out the image to a new layer. Then, click on the Indicates Layer Visibility icon in front of Layer 0 to see the cut image on Layer 1.



5. Open the destination image. Both source and destination images might be opening as tabs so drag those tabs out to make them floated. After that, drag the Layer 1 from the source image to the destination image.



Reference
https://www.youtube.com/watch?v=on5MX_h8cdI
https://www.youtube.com/watch?v=n9fwiNyDHLI

Sunday, December 9, 2018

Regular Expression in Swift 3

To string in the example below matches the pattern:


let str = "5.14152834920+3.14120395872="
let pattern = "5.14[0-9]*\\+{1}3.14[0-9]*\\={1}"
Bool match = try match(source: str, pattern: pattern)
print("They matched \(match)") // True

func match(source: String, pattern: String) throws -> Bool {
        let regex = try NSRegularExpression(pattern: pattern, options: [])
        return regex.firstMatch(in: source, options: [], range: NSMakeRange(0, source.utf16.count)) != nil
}


Patterns


- [0-9]*  means zero or more digits
- \\+{1} means one plus sign. The double backslashes escape the plus sign.
- \\={1} means one equal character

Unicode


The firstMatch() method converts the given String object to NSString object but the String's count property does not always return the same value as the NSString's length property. The former relies on extended grapheme clusters, in which two or more unicode scalars combined are considered as one character.

  1. let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
  2. // regionalIndicatorForUS is 🇺🇸

The later relies on 16-bit code units, in which one unicode scalar is considered as one character. In the case above, there are two unicode scalars so it's two characters.

Then, to tell the firstMatch() method to iterate from start to end of the string, The utf16.count property must be used instead of the count property.


Reference
https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html

Saturday, December 1, 2018

Swift's Unit Test: Comparing two floating-point numbers (in scientific notation)

Comparing two floating-point numbers in Unit test (using Swift 3's XCTAssertEqual() method) might fail if their precisions are different. For example, if the expected result is 3.3 but the actual result (of 10/3) is 3.333333333333333, the test fails. The solution is we specify the accuracy argument of the XCTAssertEqual() method to tell the framework that a small difference between their precisions do not matter.

let result = 3.3
XCTAssertEqual(result, 10/3, accuracy: 0.04)

In the code snippet above, the framework considers those two numbers are equal because they are just 0.033333333333333 different, which is less than the accuracy parameter 0.04.

Scientific Notation


In Swift, the double literal can be expressed in scientific notation like 1.234e10, which equals 12_340_000_000. The test in the example below will fail.

XCTAssertEqual(1.1234e10, 1.123e10)

To make the test successful, the accuracy parameter should be specified as following.

XCTAssertEqual(1.1234e10, 1.123e10, accuracy: 0.0004e10)
// 1.1234e10 = 11234_000_000
// 1.123e10  = 11230_000_000
// 0.0004e10 =     4_000_000

The accuracy value less than that such as 0.0003e10 or 0.0001e10 still make the test fail.




Friday, November 16, 2018

Introduction to Hyperbolic Functions (sinh, cosh, and tanh)

Terminology


A hyperbola (plural hyperbolas or hyperbolae) is an open curl lying on a plane. It has two pieces called branches or connected components. It's one of the three kinds of conic sections (such as parabola→1, the ellipse→2, and hyperbola→3), formed by the intersection of a plane and a double cone.

A cone is a three-dimensional geometric shape that tapers smoothly from a flat base to a point called apex or vertex.


A plane is a flat two-dimensional survey that extends infinitely far.


A conic section is a curve obtained from the intersection of the surface of a cone with a plane.

Some equations produce hyperbolas on the coordinate system as shown in the image below.


Hyperbolic Functions


The ordinary Trigonometric functions (sin, cos, and tan) are constructed using a unit circle but the hyperbolic functions (sinhcosh, and tanh) are constructed using unit hyperbola. The unit circle is a circle with a radius of one.


The unit hyperbolic is the set of points (x,y) in the Cartesian plane that satisfies the equation x2-y2=1. In other words,  x2-y2 always equals 1 if the point (x,y) is on the curve.


A hyperbolic angle (the triangle contained the red area), that has an area a, has a hyperbolic sector with an area half the hyperbolic angle (the red area). The hyperbolic angle a is a real number that is the argument of the hyperbolic functions (sinhcosh, and tanh). I don't know how to measure the area a, but the hyperbolic sector can be calculated (in radians) and then we just multiply it by 2 to get the area a. This video (I uploaded it to my server here) explains how to find the hyperbolic sector.


Below is the formula to calculate the hyperbolic functions. Unlike usual trigonometric functions, x is not an angle. It's the area.



Sunday, November 4, 2018

Enabling Khmer Unicode Keyboard on MacOS Sierra 10.12

You don't need to install anything on MacOS Sierra v10.12 or may be even on Mac OSX Mavericks v10.9. The Khmer Unicode keyboard layout and font (Kh Battambang) are built-in. It was created by Mr. Danh Hong (dnhhng@yahoo.com). To make it show up in the Menu bar, you have to open the Keyboard Preferences and add the keyboard named Khmer in the Input Sources as shown below.


Note that this keyboard layout does not work well with the Khmer fonts created by different developers. The issue can be missing or overlapping characters in Microsoft Offices. You have to use the fonts created by Mr. Danh Hong such as Kh Battambang (similar to Limon S1) and Kh Muol (similar to Limon R1).

Keyboard layout