How to cancel vertical scrolling on paging TabView in SwiftUI

Issue #814 From iOS 14, TabView has the PageTabViewStyle that turns TabView into the equivalent UIPageViewController. We can of course implement our own Pager but the simple DragGesture does not bring the true experience of a paging UIScrollView or paging TabView. We can also use UIPageViewController under the hood but it鈥檚 hard to do lazy. Paging TabView in iOS 14 is built int and optimized for us. We just need to specify PageTabViewStyle, or just ....

June 29, 2021 路 3 min 路 Khoa Pham

How to make simple swipe vertically to dismiss in SwiftUI

Issue #813 Use simultaneousGesture to not collide with potential horizontal scrolling in carousel view, and check that we' more accidentally swipe horizontally. import SwiftUI struct SwipeToDismissModifier: ViewModifier { var onDismiss: () -> Void @State private var offset: CGSize = .zero func body(content: Content) -> some View { content .offset(y: offset.height) .animation(.interactiveSpring(), value: offset) .simultaneousGesture( DragGesture() .onChanged { gesture in if gesture.translation.width < 50 { offset = gesture.translation } } .onEnded { _ in if abs(offset....

June 29, 2021 路 1 min 路 Khoa Pham

How to make carousel pager view in SwiftUI

Issue #812 Use GeometryReader to set width and a DragGesture on LazyVStack CarouselView( pageCount: images.count, currentIndex: $selectedImageIndex ) { ForEach(0 ..< images) { image in // AsyncImage } } struct CarouselView<Content: View>: View { let pageCount: Int @Binding var currentIndex: Int @ViewBuilder let content: Content @GestureState private var translation: CGFloat = 0 var body: some View { GeometryReader { geometry in LazyHStack(spacing: 0) { self.content.frame(width: geometry.size.width) } .frame(width: geometry.size.width, alignment: ....

June 29, 2021 路 1 min 路 Khoa Pham

How to use SwiftFormat

Issue #811 Don鈥檛 add SwiftFormat as SPM package, instead use command line directly if which swiftformat >/dev/null; then swiftformat . else echo "warning: SwiftFormaat not installed, download from https://github.com/nicklockwood/SwiftFormat" fi Then install SwiftFormat locally brew install swiftformat Then make some rules in any file name with extension .swiftformat While formatting, SwiftFormat will automatically check inside each subdirectory for the presence of a .swiftformat file and will apply any options that it finds there to the files in that directory....

June 27, 2021 路 1 min 路 Khoa Pham

How to update Firestore value with KeyPath in Swift

Issue #810 struct User { var createdAt: Date var name: Sttring var locked: Bool } extension KeyPath where Root == User { var keyPathString: String { switch self { case \User.createdAt: return "createdAt" case \User.name: return "name" case \User.locked: return "locked" default: return "" } } } Then we can call reference .collection("users") .document(user.id) .updateData([ (\User.locked).keyPathString:. true ])

June 26, 2021 路 1 min 路 Khoa Pham

How to show context menu with custom preview in SwiftUI

Issue #809 Add a hidden overlay UIContextMenuInteraction. Provide preview in previewProvider and actions in actionProvider. Use @ViewBuilder to make declaring preview easy. extension View { func contextMenuWithPreview<Content: View>( actions: [UIAction], @ViewBuilder preview: @escaping () -> Content ) -> some View { self.overlay( InteractionView( preview: preview, menu: UIMenu(title: "", children: actions), didTapPreview: {} ) ) } } private struct InteractionView<Content: View>: UIViewRepresentable { @ViewBuilder let preview: () -> Content let menu: UIMenu let didTapPreview: () -> Void func makeUIView(context: Context) -> UIView { let view = UIView() view....

June 26, 2021 路 3 min 路 Khoa Pham

How to login with Apple in SwiftUI

Issue #808 Make SignInWithAppleButton Wrap ASAuthorizationAppleIDButton inside UIViewRepresentable import SwiftUI import UIKit import AuthenticationServices struct SignInWithAppleButton: View { @Environment(\.colorScheme) private var colorScheme: ColorScheme var body: some View { ButtonInside(colorScheme: colorScheme) .frame(width: 280, height: 45) } } private struct ButtonInside: UIViewRepresentable { let colorScheme: ColorScheme func makeUIView(context: Context) -> ASAuthorizationAppleIDButton { switch colorScheme { case .dark: return ASAuthorizationAppleIDButton(type: .signIn, style: .white) default: return ASAuthorizationAppleIDButton(type: .signIn, style: .black) } } func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) { // No op } } struct SignInWithAppleButton_Previews: PreviewProvider { static var previews: some View { SignInWithAppleButton() } } Handle logic in ViewModel import SwiftUI import AuthenticationServices import Resolver import Firebase import FirebaseAuth final class AuthViewModel: NSObject, ObservableObject { enum RequestState: String { case signIn case link case reauth } let firebaseService: FirebaseService var window: UIWindow?...

June 25, 2021 路 3 min 路 Khoa Pham

How to use SwiftLint in SPM project

Issue #807 Don鈥檛 add SwiftLint via SPM, but just add a Run Script Build phrase if which swiftlint >/dev/null; then swiftlint else echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" fi Install swiftlint locally brew install swiftlint Add .swiftlint.yml with some initial disabled rules. Gradually opt-in rules to have more checks included: - MyApp - MyAppTests excluded: - Tests/SwiftLintFrameworkTests/Resources analyzer_rules: - unused_declaration - unused_import disabled_rules: - line_length - trailing_whitespace - vertical_whitespace_opening_braces - vertical_whitespace_closing_braces - void_return - identifier_name - file_name - number_separator - sorted_imports - unneeded_parentheses_in_closure_argument - redundant_optional_initialization - todo - first_where - closure_spacing - vertical_parameter_alignment_on_call - unused_optional_binding - operator_usage_whitespace - large_tuple - vertical_whitespace - multiple_closures_with_trailing_closure - control_statement - statement_position - closure_end_indentation - opening_brace - empty_count - type_body_length - force_try - force_cast opt_in_rules: - anyobject_protocol - array_init - attributes - collection_alignment - contains_over_filter_count - contains_over_filter_is_empty - contains_over_first_not_nil - contains_over_range_nil_comparison - discouraged_none_name - discouraged_object_literal - empty_collection_literal - empty_string - empty_xctest_method - enum_case_associated_values_count - explicit_init - extension_access_modifier - fallthrough - fatal_error_message - file_header - flatmap_over_map_reduce - identical_operands - joined_default_parameter - last_where - legacy_multiple - legacy_random - literal_expression_end_indentation - lower_acl_than_parent - modifier_order - nimble_operator - nslocalizedstring_key - number_separator - object_literal - overridden_super_call - override_in_extension - pattern_matching_keywords - prefer_self_type_over_type_of_self - private_action - private_outlet - prohibited_interface_builder - prohibited_super_call - quick_discouraged_call - quick_discouraged_focused_test - quick_discouraged_pending_test - reduce_into - redundant_nil_coalescing - redundant_type_annotation - single_test_class - sorted_first_last - static_operator - strong_iboutlet - test_case_accessibility - toggle_bool - unavailable_function - unowned_variable_capture - untyped_error_in_catch - xct_specific_matcher - yoda_condition file_name: excluded: - Constants....

June 24, 2021 路 2 min 路 Khoa Pham

How to show modal window in AppKit

Issue #806 Use runModal https://developer.apple.com/documentation/appkit/nsapplication/1428590-runmodalsession Blocks main queue A loop that uses this method is similar in some ways to a modal event loop run with runModal(for:), except with this method your code can do some additional work between method invocations. When you invoke this method, events for the NSWindow object of this session are dispatched as normal. This method returns when there are no more events. You must invoke this method frequently enough in your loop that the window remains responsive to events....

June 24, 2021 路 1 min 路 Khoa Pham

How to perform action during long press in SwiftUI

Issue #805 Use LongPressGesture to detect when long-press gesture has been recognized, and chain with DragGesture to check when pressing still occurs Image(systemName: SFSymbol.deleteLeft.rawValue) .imageScale(.medium) .onTapGesture { onKeyCommand(.backspace) } .gesture( LongPressGesture() .onEnded { _ in self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in self.onKeyCommand(.backspace) } } .sequenced( before: DragGesture(minimumDistance: 0) .onEnded { _ in self.timer?.invalidate() self.timer = nil } ) )

June 20, 2021 路 1 min 路 Khoa Pham

How to fallback img in React

Issue #804 <img src={user.avatarUrl} onError={(e) => { e.target.onerror = null; e.target.src = "/images/default.png" }} /> Extract to React Component interface Props { src: string className: string fallback: string } export default function FallbackImg(props: Props) { return ( <img className={props.className} src={props.src} onError={(e) => { // @ts-ignore e.target.src = props.fallback }} /> ) } How to fallback backgroundImage Use 2 url var el = document.createElement('div') el.className = 'marker' el.style.backgroundImage = `url('${user.avatarUrl}'), url('/images/user....

May 26, 2021 路 1 min 路 Khoa Pham

How to use videojs in React

Issue #803 import React from 'react'; import videojs from 'video.js' import 'video.js/dist/video-js.css'; export default class VideoPlayer extends React.Component { createPlayer() { // instantiate Video.js this.player = videojs(this.videoNode, this.props, function onPlayerReady() { //console.log('onPlayerReady', this) }); } disposePlayer() { if (this.player) { this.player.dispose() } } componentDidMount() { this.createPlayer() } componentWillUnmount() { this.disposePlayer() } componentDidUpdate(prevProps) { if (this.props.sources != prevProps.sources) { this.player.src(this.props.sources[0]) } } // wrap the player in a div with a `data-vjs-player` attribute // so videojs won't create additional wrapper in the DOM // see https://github....

May 20, 2021 路 1 min 路 Khoa Pham

How to use FontAwesome 5 in Nextjs

Issue #802 npm i --save @fortawesome/fontawesome-svg-core \ @fortawesome/free-solid-svg-icons \ @fortawesome/free-brands-svg-icons \ @fortawesome/react-fontawesome import { faApple, faGithub, faTwitter } from '@fortawesome/free-brands-svg-icons' import { faUser, faPowerOff, faAngleDown } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' <FontAwesomeIcon icon={faCoffee} />

May 19, 2021 路 1 min 路 Khoa Pham

How to use Bulma in Nextjs

Issue #801 Read https://nextjs.org/docs/basic-features/built-in-css-support#sass-support npm install bulma sass In styles/index.scss @import '~bulma/bulma'; In _app.tsx import '../styles/index.scss'

May 19, 2021 路 1 min 路 Khoa Pham

Recommended resource for learning SwiftUI

Issue #800 I鈥檝e done some #SwiftUI lately for Push Hero and PastePal, and amazed by how easy and fun it is to make apps. If you want to get into SwiftUI but don鈥檛 know where to start, here are my recommended resources Twitter https://twitter.com/onmyway133/status/1359608162899415040 Thread reader https://threadreaderapp.com/thread/1359608162899415040.html

April 30, 2021 路 1 min 路 Khoa Pham

How to make your apps stand out on the AppStore

Issue #799 Besides coming up with a good idea and building the app, there are many other things you can do to boost your apps' visibility Twitter thread https://twitter.com/onmyway133/status/1387851727714635776 Thread reader https://threadreaderapp.com/thread/1387851727714635776.html

April 30, 2021 路 1 min 路 Khoa Pham

How to use SwiftGen and LocalizedStringKey in SwiftUI

Issue #798 swiftgen.yml strings: inputs: PastePal/Resources/Localizable/en.lproj outputs: - templatePath: swiftgen-swiftui-template.stencil output: PastePal/Resources/Strings.swift Template from https://github.com/SwiftGen/SwiftGen/issues/685 swiftgen-swiftui-template.stencil // Generated using SwiftGen 鈥 https://github.com/SwiftGen/SwiftGen {% if tables.count > 0 %} import SwiftUI // MARK: - Strings {% macro parametersBlock types %}{% filter removeNewlines:"leading" %} {% for type in types %} {% if type == "String" %} _ p{{forloop.counter}}: Any {% else %} _ p{{forloop.counter}}: {{type}} {% endif %} {{ ", " if not forloop....

April 27, 2021 路 2 min 路 Khoa Pham

How to localize SwiftUI projects

Issue #797 Reference https://stackoverflow.com/questions/62092635/populating-localizable-strings-file-with-terminal-genstring-command-in-swiftui https://github.com/SwiftGen/SwiftGen https://stackoverflow.com/questions/46924196/localizable-strings-the-data-couldn-t-be-read-because-it-isn-t-in-the-correct

April 23, 2021 路 1 min 路 Khoa Pham

How to use ForEach with indices in SwiftUI

Issue #796 One seemingly obvious way to use ForEach on array with indices is using enumerated struct Book: Hashable, Identifiable { let id: UUID let name: String } struct BooksView: View { let books: [Book] var body: some View { List { ForEach(Array(books.enumerated()), id: \.element) { index, book in Text(book.name) .background(index % 2 == 0 ? Color.green : Color.orange) } } } } Reading the documentation for enumerated closely When you enumerate a collection, the integer part of each pair is a counter for the enumeration, but is not necessarily the index of the paired value....

April 20, 2021 路 2 min 路 Khoa Pham

How to resize NSImage with padding

Issue #795 extension NSImage { func resize(width: CGFloat, height: CGFloat, padding: CGFloat) -> NSImage { let img = NSImage(size: CGSize(width: width, height: height)) img.lockFocus() let ctx = NSGraphicsContext.current ctx?.imageInterpolation = .high self.draw( in: NSMakeRect(0, 0, width, height), from: NSMakeRect(0, -padding, size.width, size.height - padding), operation: .copy, fraction: 1 ) img.unlockFocus() return img } } So we can use like button.image = NSImage(named: NSImage.Name("pastePal"))?.resize(width: 22, height: 22, padding: -4)

April 15, 2021 路 1 min 路 Khoa Pham