Lately, I’ve been crafting this fantastic app PastePal that I’m thrilled to share with you today.

As someone who uses Mac, iPhone, and iPad a lot. I constantly find the need to organize my pasteboard and have it accessible across devices in a beautiful and convenient way. Plus I have dozen of data like Slack messages, JIRA tickets, token keys, color codes, and links that I want to organize the best way so I can access later. When I heard that my colleagues have the same problem and can’t find the best solution, it itches me to solve this.

🏄‍♂️…


In SwiftUI there are fixed frame and flexible frame modifiers.

Fixed frame Positions this view within an invisible frame with the specified size.

Use this method to specify a fixed size for a view’s width, height, or both. If you only specify one of the dimensions, the resulting view assumes this view’s sizing behavior in the other dimension.

VStack {
Ellipse()
.fill(Color.purple)
.frame(width: 200, height: 100)
Ellipse()
.fill(Color.blue)
.frame(height: 100)
}

Flexible frame frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)

Read the documentation carefully

Always specify at least one size characteristic when calling this method. Pass nil or leave out a characteristic to indicate that the frame should adopt this view’s sizing behavior, constrained by the other non-nil arguments.

The size proposed…


Use NSTextField with maximumNumberOfLines

import AppKit
import SwiftUI
struct AttributedText: NSViewRepresentable { let attributedString: NSAttributedString init(_ attributedString: NSAttributedString) {
self.attributedString = attributedString
}
func makeNSView(context: Context) -> NSTextField {
let textField = NSTextField()
textField.lineBreakMode = .byClipping
textField.maximumNumberOfLines = 0
textField.isBordered = false
return textField
}
func updateNSView(_ nsView: NSTextField, context: Context) {
nsView.attributedStringValue = attributedString
}
}

TextField has problem with wrapping, we can use TextView

struct AttributedTextView: NSViewRepresentable {
typealias NSViewType = NSScrollView
let attributedText: NSAttributedString?
let isSelectable: Bool
var insetSize: CGSize = .zero
func makeNSView(context: Context) -> NSViewType {
let scrollView = NSTextView.scrollableTextView()
let textView = scrollView.documentView…


NSTextView has this handy method to make scrollable NSTextView NSTextView.scrollableTextView(). The solution is to get to the responder outside enclosing NSScrollView, in my case it is the SwiftUI hosting view


Use @ViewBuilder to build dynamic content for our HUD. For blur effect, here I use NSVisualEffectView, but we can use .blur modifier also

struct HUD<Content>: View where Content: View {…


With Xcode 12, we can fire up Instrument to profile our app. Select SwiftUI template

There are many profiles in that template, I find SwiftUI and Time Profile very useful. Here’s the profile I run for my app PastePal

SwiftUI View Body

This shows how many instance of View with body invocation are there, both for SwiftUI views and our app views

Taking a look at SwiftUI profile, it shows that ClipboardCell is taking most of the time, here over 7 seconds


@propertyWrapper struct UserDefault<Value> { let key: String let defaultValue: Value let container: UserDefaults = .standard var wrappedValue: Value { get { return container.object(forKey: key) as? Value ?? defaultValue } set…


Use NSSharingService.sharingServices(forItems:) with an array of one empty string gives a list of sharing items. There we show image and title of each menu item

import SwiftUI import AppKit import…

I have an enum that conforms to CaseIterable that I want to show in Picker

enum Position: String, Codable, CaseIterable, Identifiable {
var id: String { rawValue }
case left
case right
case bottom
case top
}
Picker(selection: $preference.position, label: Text("Position")) {
ForEach(Preference.Position.allCases) { position in
Text(position.rawValue)
}
}

It compiles and runs just fine, but Picker does not show current selection regardless of any Picker style I choose. It does not update Binding at all.

The fix is to specify id, it looks redundant because of enum conforms to Identifiable, but it fixes the problem

Picker(selection: $preference.position, label: Text("Position"))…

Khoa Pham

Check out my apps https://onmyway133.com/apps

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store