5 May 2018

5 months after I last worked on it, I’m finally releasing Orbit: a MetaWeblog API server that allows you to publish to your Hugo site from MarsEdit. https://github.com/elliotekj/orbit

23 December 2017

IMG 1169

The peaks are looking particularly nice today.

22 December 2017

Swift: Create a UIButton with background and border gradients, rounded corners and a shadow

The final button

The standard UIButton is a fairly bland affair. We don’t need to write and inordinate amount of code to spruce it up though. In this tutorial we’ll look at how to add background and border gradients, round its corners and display a drop shadow underneath it.

Note: If you just want the code and aren’t bothered about the explanations, you’ll find a complete subclass at the bottom of this tutorial.

Setup

We’re going to be styling our UIButton via a subclass so that the design can be trivially reused and updated throughout the app.

Start by creating a new “Cocoa Touch Class” file:

  • Class: I’m naming mine PrimaryButton. You can name yours whatever you want, just make sure you replace PrimaryButton with whichever name you chose as you follow along.
  • Subclass of: UIButton
  • Language: Swift

Next, add a button object to the ViewController of your choosing. With the button selected, open the Identity Inspector tab in the sidebar and change the button’s class to PrimaryButton.

That’s all the setup done. We can now get on with the styling.

Styling

We’re going to be working in PrimaryButton.swift for the remainder of this tutorial so open that up now. You can delete the commented out draw method.

All of our modifications to the generic UIButton are going to be done in the layoutSubviews method, so start by overriding that in the class.

override func layoutSubviews() {
    super.layoutSubviews()

    // All remaining snippets go here…
}

The border gradient

We’ll start by implementing the border gradient as all our other additions are going to be placed below it. There are two parts to a border gradient: the first is the CAGradientLayer that, as you might expect, draws the gradient; the second is a mask that defines where the gradient will be visible. If we didn’t create a mask, the gradient layer would take up the whole of the button.

The first portion of the following snippet draws the aforementioned CAGradientLayer; the second portion defines the mask. It’s the lineWidth property on borderMask that determines how thick the stroke around the button will be.

I think frame is the only property here that isn’t self-explanatory. We’ll be seeing it throughout this post so we might as well explain it here. frame simply defines the position and size of a layer. Here, borderGradient.frame = self.bounds translates to “set the position and size values of borderGradient to be the same as the position and size values of the button”.

// Border:

let borderGradientTop = UIColor(hue:0.72, saturation:0.59, brightness:0.90, alpha:1.00).cgColor
let borderGradientBottom = UIColor(hue:0.72, saturation:0.82, brightness:0.90, alpha:1.00).cgColor
let borderGradient = CAGradientLayer()
borderGradient.frame = self.bounds
borderGradient.colors = [borderGradientTop, borderGradientBottom]
borderGradient.cornerRadius = 4

let borderMask = CAShapeLayer()
borderMask.lineWidth = 1
borderMask.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 4).cgPath
borderMask.fillColor = UIColor.clear.cgColor
borderMask.strokeColor = UIColor.black.cgColor

borderGradient.mask = borderMask
self.layer.insertSublayer(borderGradient, below: self.titleLabel?.layer)

// Next snippet goes here…

The background gradient

The code for the background is almost an exact copy of the first portion of the above snippet, we just need to change the colours.

// Background:
let backgroundGradientTop = UIColor(hue:0.72, saturation:0.58, brightness:0.97, alpha:1.00).cgColor
let backgroundGradientBottom = UIColor(hue:0.72, saturation:0.80, brightness:0.96, alpha:1.00).cgColor
let background = CAGradientLayer()
background.frame = self.bounds
background.colors = [backgroundGradientTop, backgroundGradientBottom]
background.cornerRadius = 4
self.layer.insertSublayer(background, below: borderGradient)

// Next snippet goes here…

The shadow

We can add the shadow via the UIButton’s layer property. No need to create another sublayer.

Whilst not particularly intuitive, the width and height values of shadowOffset are in fact what control the shadow’s x and y positions.

// Shadow:
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0.5)
self.layer.shadowOpacity = 0.2
self.layer.shadowRadius = 1
self.layer.masksToBounds = false

// Next snippet goes here…

The text

If you try running your app now, the button should look great… except for the text. Let’s finish up here by polishing that.

// Text:
self.setTitleColor(UIColor.white, for: .normal)
self.setTitle(self.titleLabel?.text?.uppercased(), for: .normal)
self.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .bold)

Final Subclass

Here’s what your final class should look like:

import UIKit

class PrimaryButton: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        // Border:

        let borderGradientTop = UIColor(hue:0.72, saturation:0.59, brightness:0.90, alpha:1.00).cgColor
        let borderGradientBottom = UIColor(hue:0.72, saturation:0.82, brightness:0.90, alpha:1.00).cgColor
        let borderGradient = CAGradientLayer()
        borderGradient.frame = self.bounds
        borderGradient.colors = [borderGradientTop, borderGradientBottom]
        borderGradient.cornerRadius = 4

        let borderMask = CAShapeLayer()
        borderMask.lineWidth = 1
        borderMask.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 4).cgPath
        borderMask.fillColor = UIColor.clear.cgColor
        borderMask.strokeColor = UIColor.black.cgColor

        borderGradient.mask = borderMask
        self.layer.insertSublayer(borderGradient, below: self.titleLabel?.layer)

        // Background:
        let backgroundGradientTop = UIColor(hue:0.72, saturation:0.58, brightness:0.97, alpha:1.00).cgColor
        let backgroundGradientBottom = UIColor(hue:0.72, saturation:0.80, brightness:0.96, alpha:1.00).cgColor
        let background = CAGradientLayer()
        background.frame = self.bounds
        background.colors = [backgroundGradientTop, backgroundGradientBottom]
        background.cornerRadius = 4
        self.layer.insertSublayer(background, below: borderGradient)

        // Shadow:
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 0, height: 0.5)
        self.layer.shadowOpacity = 0.2
        self.layer.shadowRadius = 1
        self.layer.masksToBounds = false

        // Text
        self.setTitleColor(UIColor.white, for: .normal)
        self.setTitle(self.titleLabel?.text?.uppercased(), for: .normal)
        self.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .bold)
    }

}

I tried to keep things as simple as possible here, but once you’ve got this working I’d recommend creating a variable at the top of the function to manage all of the cornerRadius properties.

Conclusion

I hope this was of use to you. I’m still a huge fan of “buttons that look like buttons” both from an aesthetics standpoint and, more importantly, from a UX standpoint. If this tutorial helps transform even one UIButton back into a “button that looks like a button” then I’ll be happy.

Next time we’ll look at how to update the button’s design as its state changes.

19 December 2017

Realmac are running App Santa again this year.

My pick from the apps on sale is Paste 2, a beautifully designed clipboard manager for the Mac that I’ve been using daily since it was first released in 2015.

After hearing so much about it, I myself finally bought Halide to play with over the holidays.

15 December 2017

In which Lukas Reschke explains step-by-step how he got remote code to execute in Atom via Cross-Site Scripting (the specific vulnerability he found was patched in v1.21.1).

Atom happens to be the single most popular Electron project on GitHub. I suspect that, were a study to be done, an large number of Electron apps would be found to be vulnerable to XSS attacks in some shape or form. What makes this angle of attack particularly bad for Electron apps is that injected JavaScript, just like the JavaScript the app’s developer wrote, has full access to the NodeJS core. Lukas demonstrated this by launching the Calculator app via a child process; its not hard to think up something far more destructive (or discreet) to run once you have this much access though:

One easy way to [execute malicious JavaScript code], in this case, is by accessing the window.top object and use the NodeJS require function to access the child_process module. The following JavaScript call would open the Mac OS X calculator:

window.top.require('child_process').execFile('/Applications/Calculator.app/Contents/MacOS/Calculator',function(){});
14 December 2017

A paper by Lance Spitzner from back in 2003 in which he explains honeytokens, their huge power to simplicity ratio, and provides some good examples.

My highlights

The term honeytoken was first coined by Augusto Paes de Barros in 2003 on the honeypots mailing list.

[…]

A honeytoken can be a credit card number, Excel spreadsheet, PowerPoint presentation, a database entry, or even a bogus login. Honeytokens come in many shapes or sizes, however they all share the same concept: a digital or information system resource whose value lies in the unauthorized use of that resource. Just as a honeypot computer has no authorized value, no honeytoken has any authorized use.

[…]

For example, the credit card number 4356974837584710 could be embedded into database, file server, or some other type of repository. The number is unique enough that there will be minimal, if any, false positives. An IDS signature, such as Snort, could be used to detect when that honeytoken is accessed. Such a simple signature could look as follows.

alert ip any any -> any any (msg:"Honeytoken Access - Potential Unauthorized Activity";   content:"4356974837584710";)  

This concept can easily be extended beyond databases. File, web, or email servers can all have honeytokens embedded into them. Anything that has data can easily have additional bogus data added, bogus data that becomes our honeytoken.

13 December 2017

The UX of vague user input and educated guesses

I got an Amazon Echo Dot a couple of months ago — my first voice assistant and the only one I’ve used. I’ve found it to be largely underwhelming on the software front. Maybe I just set my expectations too high because of all the wonderful things the people I follow have been saying about it since its release. The following is neither a rant about nor a full review of the software. It is merely my thoughts on how a specific though frequently used bit of functionality could be improved.

Like many, I keep my Echo in the kitchen. It’s the ideal environment for voice assistants in general and the Echo in particular to shine. Want to play or pause music whilst your hands are covered in flour? Shout at the Echo. Need to add something to the shopping list? Shout at the Echo. Need a timer? Shout at the Echo. Want to be reminded about something? Shout at the Echo.

It’s that last interaction and the ones like it that I feel have huge room for improvement.

Scene: It’s early afternoon and you’ve just received a text from a friend asking if you’re up for a gym session that evening. You are, so you ask your Echo to remind you about it later.

You: “Alexa, remind me to go to the gym at five-thirty.”

A perfect response to this would be something along the lines of “Okay, I’ll remind you”. What we get instead is…

Alexa: “Is that five-thirty in the morning or in the afternoon?”

…which is a reasonable enough clarification to ask for. The issue I have with it is that every time the user forgets to specify whether the reminder is intended for the morning or the afternoon, they are blocked at this point and have to wait for Alexa to stop speaking in order to respond.

The UX of this can be vastly improved by making an educated guess based on the context. What do we know? We know that it’s early afternoon. We know from previous reminders that the user tends to schedule activities of this nature for late afternoon / early evening. Depending on whether or not the user specifies the name of the gym, we may even know that said gym doesn’t open until 9am. From that context, an educated guess can be made that the user means 5:30pm.

Alexa may well get the occasional guess wrong though, which is why you’d append “Did you mean 5:30am?” to the confirmation. That way instead of the above we wind up with this.

You: “Alexa, remind me to go to the gym at five-thirty.”

Alexa: “Okay, I’ll remind you at 5:30 this afternoon. Did you mean 5:30am?”

…and if you did mean “am”…

You: “Yes.”

Alexa: “Okay, I’ll remind you at 5:30am instead.”

With this new way of specifying morning or afternoon, the user can stop listening at the end of the first part of the confirmation if Alexa guessed correctly. Only if the guess was wrong does the user need to have any more interaction with the device, thus completely avoiding both the lingering necessary before “morning” or “afternoon” can be clarified and the need to clarify at all.

At this level, interaction changes that can save the user multiple seconds every time aren’t a dime a dozen. Pick that low hanging fruit.

12 December 2017

Canarytokens is a very cool looking tool built by Thinkist that allows you to easily — and freely — generate and monitor honeytokens.

These tripwires can be set off in a number of ways of your choosing, from a simple GET request all the way to triggering when a specific query is run on your MySQL database. SELECT * FROM user_passwords for example.

A number of helper tools are provided for use with Canarytokens, all of which are described in the linked blog post. The two that seem most interesting to me are the aforementioned MySQL trigger and the FileWatcher trigger which notifies you when a specific file is read.

Canarytokens is open source and self-hosting is made easy thanks to an official Docker image.

10 December 2017

Redesign preview

Knackered my foot at practice on Friday so I’ve spent the weekend tinkering with various projects including a potential update for my site. Nowhere near done but I’m pleased with how the header came out.

9 December 2017

This post was written in MarsEdit and is destined for a static blog that’s generated by Hugo. Once I click “Send to Blog” the rest should be handled by the server, which’ll create the markdown file, regenerate the site, sync the new markdown file to Google Drive and finish up by pinging Micro.blog so that the post shows up in a timely manner. Let’s see if this works…

8 December 2017

Progress report on Hugo + MarsEdit: all post frontmatter is being parsed, loaded and saved correctly, posts are displayed in the correct order & post editing works.

6 December 2017

My imagination or do blogrolls seem to be slowly creeping their way back onto people’s sites? I’ve seen quite a few recently, but that may just be down to the fact that I’ve been visiting more blogs written by IndieWeb folk.

You can keep up to date with new posts by subscribing to the RSS Feed or by following me on Micro.blog.