vultr 2017-02-21.png
This page contains an uploaded file:
No summary was provided for this file.
This page contains an uploaded file:
No summary was provided for this file.
This page contains an uploaded file:
No summary was provided for this file.
This page contains an uploaded file:
No summary was provided for this file.
This page contains an uploaded file:
No summary was provided for this file.
Recently I had the need to show a logo in SVG format, but the project required that we did not include 3rd party libraries. The following Swift playground shows how you can show an SVG-based image using WKWebView.
Somehow, you need to know the image size beforehand -- or at least you need to be able to set width and height constraints. Lots of logos are square, thus there's no need to do anything special.
import UIKit import PlaygroundSupport import WebKit
class MyViewController : UIViewController { override func loadView() { let view = UIView() view.backgroundColor = .white
let webView = WKWebView() webView.translatesAutoresizingMaskIntoConstraints = false
let header = """ <!DOCTYPE html><html style=\"overflow: hidden\"> <head> <meta name="viewport" content="initial-scale=1.0" /> <title>icon_people_search</title> </head> <body style=\"margin: 0;\"> """ let footer = """ </body> """ let svg = """ <?xml version="1.0" encoding="iso-8859-1"?> <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 495 495" style="enable-background:new 0 0 495 495;" xml:space="preserve"> <g> <polygon style="fill:#1A6FB0;" points="247.5,0 247.5,40 455,40 455,455 247.5,455 247.5,495 495,495 495,0 "/> <polygon style="fill:#1E81CE;" points="40,455 40,40 247.5,40 247.5,0 0,0 0,495 247.5,495 247.5,455 "/> <path style="fill:#1E81CE;" d="M205.767,405h65.266V247.413h43.798c0,0,4.104-25.428,6.103-53.235h-49.647v-36.264 c0-5.416,7.109-12.696,14.153-12.696h35.564V90h-48.366c-68.478,0-66.872,53.082-66.872,61.009v43.356h-31.771v53.029h31.771V405z" /> </g> </svg> """ webView.loadHTMLString(header + svg + footer, baseURL: Bundle.main.bundleURL) view.addSubview(webView) let constraints = [ webView.widthAnchor.constraint(equalToConstant: 100), webView.heightAnchor.constraint(equalToConstant: 100), webView.centerXAnchor.constraint(equalTo: view.centerXAnchor), webView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ] view.addConstraints(constraints) self.view = view } } // Present the view controller in the Live View window PlaygroundPage.current.liveView = MyViewController()
On iPhone, you usually add a cancel/close button to a modal popover. On iPad, there's usually no need to do so. Users just tap outside the popover to dismiss it. However when you're building an app for the iPad, and you support Split View and Multitasking, you suddenly do need it.
The following viewcontroller will display a popover, and if necessary a close button will be added.
class PresentingViewController: UIViewController, UIPopoverPresentationControllerDelegate { // MARK: - UIPopoverPresentationControllerDelegate func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { return .fullScreen } func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { guard let navigationController = controller.presentedViewController as? UINavigationController else { fatalError("Unexpected type of controller") } let closeButton = UIBarButtonItem(title: "Close", style: .done, target: self, action: #selector(close)) navigationController.topViewController?.navigationItem.leftBarButtonItem = closeButton return navigationController } @objc func close() { self.dismiss(animated: true, completion: nil) } // MARK: - View cycle @objc func popoverAction() { let pvc = PopoverViewController() let navigationController = UINavigationController(rootViewController: pvc) navigationController.modalPresentationStyle = .popover navigationController.popoverPresentationController?.delegate = self navigationController.popoverPresentationController?.sourceView = self.view navigationController.popoverPresentationController?.permittedArrowDirections = .up navigationController.popoverPresentationController?.sourceRect = CGRect(x: 40, y: 40, width: 1, height: 0) self.present(navigationController, animated: true, completion: nil) }
override func viewDidLoad() { self.view.backgroundColor = UIColor.yellow
let barButtonItem = UIBarButtonItem(title: "Popover", style: .plain, target: self, action: #selector(popoverAction)) self.navigationItem.leftBarButtonItem = barButtonItem } }
class PopoverViewController: UIViewController { override func viewDidLoad() { self.view.backgroundColor = UIColor.green } }
For historic accuracy, below you can find the manual solution. It's mainly obsolete.
class PresentingViewController: UIViewController { private var popoverViewController: PopoverViewController? override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { self.popoverViewController?.doneButtonHidden = (self.traitCollection.horizontalSizeClass == .regular) } @objc func popoverAction() { let pvc = PopoverViewController() pvc.doneButtonHidden = (self.traitCollection.horizontalSizeClass == .regular) let navigationController = UINavigationController(rootViewController: pvc) navigationController.modalPresentationStyle = .popover navigationController.popoverPresentationController?.sourceView = self.view navigationController.popoverPresentationController?.permittedArrowDirections = .up navigationController.popoverPresentationController?.sourceRect = CGRect(x: 40, y: 40, width: 1, height: 0) self.present(navigationController, animated: true, completion: nil) self.popoverViewController = pvc } override func viewDidLoad() { self.view.backgroundColor = UIColor.yellow let barButtonItem = UIBarButtonItem(title: "Popover", style: .plain, target: self, action: #selector(popoverAction)) self.navigationItem.leftBarButtonItem = barButtonItem } }
class PopoverViewController: UIViewController { var doneButtonHidden: Bool = false { didSet { let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissAction)) self.navigationItem.leftBarButtonItem = self.doneButtonHidden ? nil : button } } @objc func dismissAction() { self.dismiss(animated: true, completion: nil) } override func viewDidLoad() { self.view.backgroundColor = UIColor.green } }
This works, but requires that you keep a reference to the PresentedViewController, to update the visibility of the close button. I don't much like having a class member variable when it could be a local variable, because it clutters up the code of the PresentingViewController. But this is the most concise and readable code I could come up with.
Note that when you use child ViewControllers, this does not seem to work. The reason is that traitCollectionDidChange() doesn't get called automatically. In that case, it could be acceptable to use viewDidLayoutSubviews().
After upgrading to Xcode 9.1 (build 9B55), the following error would be shown in a modal dialog after opening an Xcode project:
An unknown error occurred
the path 'xxxxxx' exists but is not a tree (-3)
This particular path is included via a git submodule, but I'm not sure if that's related. The problem is fixed by removing the path its reference from Xcode, then add it again.
Here's a little playground that shows how to use reduce in Swift, specifically use it to report on a bunch of booleans. In the following code, we use reduce to determine whether all objects are enabled, or whether at least one is enabled.
struct MyStruct { var enabled: Bool var text: String }
let collection = [ MyStruct(enabled: true, text: "one"), MyStruct(enabled: false, text: "two"), MyStruct(enabled: true, text: "three"), ]
let enabledArray = collection.map { $0.enabled }
let allEnabled = enabledArray.reduce(true) { $0 && $1 } print(allEnabled)
let oneEnabled = enabledArray.reduce(false) { $0 || $1 } print(oneEnabled)
I've used this when I had a bunch of UITextField instances. Some of them were enabled, some not (i.e. property isEnabled set to true or false). Or for example when you have a bunch of UIView instances; are none of them hidden? That sort of stuff.
I could've posted this example with just an array of booleans as input, but I wanted to demonstrate the map as well. Often you don't just have a bare array of booleans.
One minor thing with the allEnabled variable in the example is that its result is meaningless when applied to an empty array. It'll return true. But what does that mean, right? You'll have to decide for yourself.
Sometimes, you need to reset the display under macOS, without it being visible. Under Linux, this is incredibly easy; CTRL-ALT-F1 usually gives you a text console. You can then jump back to the GUI with alt-Left. Under macOS, this is slightly harder.
This does require preparation. First, you have to make a shortcut key to System Preferences:
In the future, it'll help you as follows: assuming you're logged in and for some reason don't have a display, do the following procedure:
Here's a nice link to the most popular Swift-related projects on Github:
https://github.com/search?utf8=%E2%9C%93&q=stars%3A%3E1+language%3Aswift&type=Repositories
I couldn't find a nice, compact example of a closure typealias in Swift, so here is one you can paste straight into a Playground:
import Foundation
typealias MakeClosure = (_ a: Int, _ b: Int) -> Int
class IntFactory { static let instance = IntFactory() var makeClosure: MakeClosure?
func makeInt(a: Int, b: Int) -> Int { guard let closure = self.makeClosure else { fatalError() }
let result = closure(a, b) return result }
private init() { } }
func sum(a: Int, b: Int) -> Int { let result = a + b return result }
IntFactory.instance.makeClosure = sum IntFactory.instance.makeInt(a: 22, b: 20)
// This also works
let sum2: MakeClosure = { (a: Int, b: Int) in a + b }
IntFactory.instance.makeClosure = sum2 IntFactory.instance.makeInt(a: 22, b: 20)
For repeated calling of a function, the Timer class is available (used to be called NSTimer). So perhaps you had the following code in your Swift 3 project:
private var timer: Timer?
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.timer = Timer.scheduledTimer( timeInterval: 1.0, target: self, selector: #selector(self.timerAction), userInfo: nil, repeats: true) }
func timerAction() { // Do something }
When you move your project to Swift 4 (via Xcode 9), then first you get the following warning:
The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the "Swift 3 @objc Inference" build setting to "Default" for the "blah" target.
You can fix this warning by updating the project settings and update the code as follows:
@objc func timerAction() { // Do something }
For some reason, I don't like seeing that @objc in my projects. There's an alternative, and that's not to use Timer at all, but instead move to GCD:
private let timer = DispatchSource.makeTimerSource()
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) timer.schedule(deadline: .now(), repeating: 1.0) timer.setEventHandler { DispatchQueue.main.sync { self.timerAction() } } timer.activate() }
Have fun with Swift 4 :)
The following code can be pasted into a Swift playground, and demonstrates how you can get a mutable array of (a copy of the) dictionary keys.
let dict = ["Fritz": "Senior Engineer", "Mary": "Director of Safety", "John": "VP of this and that"]
var keyArray: [String] = dict.keys.map { $0 } print(keyArray) if let index = keyArray.index(of: "Fritz") { keyArray.remove(at: index) }
print(keyArray)
If you don't cast to an array of Strings, you'll get the following error:
error: value of type 'LazyMapCollection<LazyMapCollection<Dictionary<String, String>, String>, String>' has no member 'remove'
Sometimes, Mac software is distributed as an installer. Potentially that leaves a bunch of files strewn all over your Mac its filesystem. One tip is to run the following command before installation:
$ find -x . / > before.txt
Then install the software, and run it once. Quit, then run the following two commands:
$ find -x . / > after.txt $ diff before.txt after.txt > filelist.txt
Now open the filelist.txt and you'll see a rough list of all installed files. Both as a result of the installation, but also takes into account whatever the software wrote in your ~/Library folder.
When you hide/unhide an element in a UIStackView, it will nicely animate. However when you hide the only element in the stackview, the animation will go wrong. To see what happens, run the following project:
https://github.com/bvankuik/NestedStackViewsShowAndHide
Click the "Toggle Visibility" button. Then tap the "Add dummy view" button and try again.
When creating a custom view, I find it very useful to make it into a framework and include an example project. This enables quickly iterating over the new component. Plus, when you're done, it's very easy to import the result into other projects. To set this up, take the following steps:
Here's a little Swift playground that shows how you can check whether a Data object (for example from a file) is a PDF. It's done by checking the first couple of bytes.
import UIKit
// http://stackoverflow.com/a/26503955/1085556 func dataWithHexString(hex: String) -> Data { var hex = hex var data = Data() while(hex.characters.count > 0) { let c: String = hex.substring(to: hex.index(hex.startIndex, offsetBy: 2)) hex = hex.substring(from: hex.index(hex.startIndex, offsetBy: 2)) var ch: UInt32 = 0 Scanner(string: c).scanHexInt32(&ch) var char = UInt8(ch) data.append(&char, count: 1) } return data }
struct HeaderError: Error { }
// http://stackoverflow.com/a/17280876/1085556 let smallestPDFasHex = "255044462D312E0D747261696C65723C3C2F526F6F743C3C2F50616765733C3C2F4B6964735B3C3C2F4D65646961426F785B302030203320335D3E3E5D3E3E3E3E3E3E" let data = dataWithHexString(hex: smallestPDFasHex)
let headerRange: Range<Data.Index> = 0..<4 let header = data.subdata(in: headerRange) guard let headerString = String(data: header, encoding: .ascii) else { print("Header not found") throw HeaderError() }
if headerString == "%PDF" { print("It's a PDF") } else { print("It's NOT a PDF") }
Here's a nice way to play with a UIStackView. Copy and paste the following code into a playground, and next to the final line, click the screen icon to permanently show it. Then adjust the parameters of the playground where necessary.
import UIKit
var view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = UIColor.yellow
let label1 = UILabel() label1.text = "label 1"
let label2 = UILabel() label2.text = "label 2"
let spacer1 = UIView() spacer1.backgroundColor = UIColor.green spacer1.translatesAutoresizingMaskIntoConstraints = false spacer1.widthAnchor.constraint(equalToConstant: 3).isActive = true
let spacer2 = UIView() spacer2.backgroundColor = UIColor.gray
let stackView = UIStackView(arrangedSubviews: [label1, spacer1, label2, spacer2]) stackView.distribution = .fill stackView.axis = .horizontal stackView.translatesAutoresizingMaskIntoConstraints = false stackView.spacing = 20
view.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
view.layoutIfNeeded() view
The result should look something like this:
There's a larger and larger number of websites that auto-play completely unrelated videos when opening a page on their website. In my opinion, a very user unfriendly business.
Thankfully Firefox has an extension for this, FlashStopper: https://addons.mozilla.org/en-US/firefox/addon/flashstopper/
Before and after installing, open for example this page on the Reuters website: http://www.reuters.com/article/us-twitter-lawsuit-idUSKBN1782PH
For my current project, the user needs to select a template from a list. The template contains a number of placeholders where text needs to be filled in, and further editing needs to be done.
I've created an example Xcode project on GitHub which shows how this can be done:
Here are my short notes on creating a Linux VPS (virtual private service) which can be remotely accessed via TeamViewer. I prefer TeamViewer over other ways of remotely accessing a desktop, because it works cross-platform and through NAT and firewalls.
The big problem is often that a Linux VPS doesn't have a virtual graphical card. For remote access to the GUI, most VPS providers advise VNC while I prefer TeamViewer.
I've tried a number of Linux distributions on a number of VPS providers. The instructions below fail on VPSes at Scaleway or DigitalOcean, but the combination of Fedora 25 and Linode or Vultr made this all very easy.
First of all, register or log in at Linode or Vultr, and create a VPS. You'll need at least 1 GB of memory.
After it's started, log in via SSH as root and do the following:
# yum -y groupinstall "Fedora Workstation"
Then get the TeamViewer download URL and install the package:
# wget "https://download.teamviewer.com/download/teamviewer.i686.rpm" # yum -y install ./teamviewer.i686.rpm
Next reboot, make sure the GUI starts:
# systemctl set-default graphical.target
If your VPS runs at Vultr, disable Wayland. Edit the file /etc/gdm/custom.conf and remove the comment for the line WaylandEnable=false so GDM uses X.org. Linode already has this correctly set.
Usually you'll also want to add swap space; this adds a gig:
# dd if=/dev/zero of=/swapfile1 bs=1024 count=$1024*1024] # mkswap /swapfile1 # swapon /swapfile1 # chmod 600 /swapfile1
Add following line to /etc/fstab:
/swapfile1 swap swap defaults 0 0
Then, add a user for yourself:
# useradd -m -U mynewusername # passwd mynewusername
Add the new user to the sudoers, and reboot:
# visudo # reboot
If you're on Linode, open remote access via glish, which is Linode's way of giving you graphical access to your VPS, through your browser. Log into the Linode management console, click on the node, then in the tab Remote Access, click the link "Launch Graphical Web Console".
If you're on Vultr, open remote access by navigating to the Instances tab in the management console, then click the three dots at the right of your server and in the menu, click "View Console":
You should see the graphical Linux login screen. In the top left corner, set shell to "Gnome Xorg" (this is important!) and then continue to log into Gnome.
In Gnome, start TeamViewer. Check the three boxes to enable remote access. You'll need to provide TeamViewer username/password, as well as click the link in the email you'll get, to confirm adding this VPS to your TeamViewer device list.
Done!
Note: a current disadvantage is that you're constricted to pretty low resolutions. On Linode, you can't set the resolution to something bigger than 1280x768. On Vultr, the maximum is 1280x1024.
Recently I wanted to test how long the battery of my 2013 MacBook Air lasts. The quickest solution I found, is as follows:
Now to test the battery:
So this is my ghetto battery test. It's a bit rough around the edges, but should give you an idea of how many hours the battery lasts. Don't take the results too serious, this is meant to get a ballpark figure.
Here's an example of an AppDelegate.swift, for those who don't like Interface Builder:
import UIKit
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds) let vc = ViewController() let nc = UINavigationController(rootViewController: vc) self.window?.rootViewController = nc self.window?.makeKeyAndVisible() return true }
}
Remove the Main.storyboard file from the project. And in the Info.plist of your project, remove the entry called "Main storyboard file base name:
I couldn't find a one-page PDF with all macOS Finder shortcut keys. So I made one: Finder shortcuts.pdf. Here is the original, so you can adjust to your liking: Finder shortcuts.xlsx. Both are licensed under the Creative Commons Attribution 4.0 International License.