I'm making a new Swift (3.0) framenwork which involves the use of a NSSearchField programmatically created, like the rest of the views.
This framenwork has the deployment target set to 10.10 and I found strange being not able to set the delegate metods for it. In fact NSSearchFieldDelegate only appear availabe in 10.11 on.
class PlistEditor: NSObject, NSOutlineViewDelegate, NSOutlineViewDataSource, NSTextFieldDelegate, NSSearchFieldDelegate {
var searchField : NSSearchField?
// more code..
init(tabItem: NSTabViewItem,
plistPath: String?) {
// more code..
let sf = NSSearchField(frame: NSMakeRect(80, 0, 80, 22))
self.searchField? = sf
// more code..
super.init()
// more code..
if #available(OSX 10.11, *) {
self.searchField?.delegate = self
} else {
// Fallback on earlier versions
}
// more code
}
}
Ok, I thought it was inherent from NStextField, and maybe I can access the cell and set up using the superclass delegate, but unfurtunately I cannot found a way to do that.
What I need is to be able to receive NSText/NSTextField notification in 10.10. How can I do this?
EDITED:
added more info on how is made plus some picts
Without providing more info, i am guessing you forgot to declare that your class is conforming to the NSSearchFieldDelegate.
See the example below, how to set it up for example a viewController. You just create an extension for your vc, and declare it to conform to the delegate.
class SearchViewController: NSViewController {
let searchField: NSSearchField? = NSSearchField(frame: .zero)
override func viewWillAppear() {
super.viewWillAppear()
if #available(OSX 10.11, *) {
self.searchField?.delegate = self
} else {
// Fallback on earlier versions
}
}
}
extension SearchViewController: NSSearchFieldDelegate {
func searchFieldDidStartSearching(_ sender: NSSearchField) {
print("didStart")
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
print("didEnd")
}
}
EDIT:
To capture the textDidChange event in earlier versions of Mac OS, you need to subclass NSSearchField and override textDidChange function. Every time a change happens in the searchField, it will call the function for you.
class DLSearchField: NSSearchField {
override func textDidChange(_ notification: Notification) {
Swift.print("textDidChange")
}
}
Related
I want to generate vibration when I press the button but I get no results.
Helper class I created to manage vibrations:
import Foundation
import UIKit
final class HapticsManager{
static let shared = HapticsManager()
private init(){}
public func selectionVibrate(){
DispatchQueue.main.async {
let selectionImpactGenerator = UIImpactFeedbackGenerator()
selectionImpactGenerator.prepare()
selectionImpactGenerator.impactOccurred()
}
}
public func haptic(for type: UIImpactFeedbackGenerator.FeedbackStyle){
DispatchQueue.main.async {
let notificationGenerator = UIImpactFeedbackGenerator()
notificationGenerator.prepare()
notificationGenerator.impactOccurred()
}
}
}
in ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
HapticsManager.shared.selectionVibrate()
addTargets()
setStartGradientView()
showLayout()
}
Function of button with click target added:
#objc fileprivate func setButtonClicked(){
HapticsManager.shared.haptic(for: .heavy)
}
I tried many methods but no result.
Thanks...
There is no problem about your code but needs some additional information. You need to check if device hardware is support for CHHapticEngine like that;
public func haptic(for type: UIImpactFeedbackGenerator.FeedbackStyle) {
if CHHapticEngine.capabilitiesForHardware().supportsHaptics {
let notificationGenerator = UIImpactFeedbackGenerator()
notificationGenerator.prepare()
notificationGenerator.impactOccurred()
} else {
AudioServicesPlaySystemSound(1520)
}
}
I've tried, without success, to use the new property wrapper #FocusedBinding.
The code example given here by a Frameworks Engineer, and placed below, during beta 1 period for iOS 14 and Big Sur compiles, but it doesn't seem to work for both OSs, for enabling the keyboard shortcuts.
Does anyone knows if something changed in the meantime, and how, or is something still under development?
// This example runs on macOS, iOS, and iPadOS.
//
// Big Sur Seed 1 has some known issues that prevent state-sharing between
// windows and the main menu, so this example doesn't currently behave as
// expected on macOS. Additionally, the Commands API is disabled on iOS in Seed
// 1. These issues will be addressed in future seeds.
//
// The Focused Value API is available on all platforms. The Commands and
// Keyboard Shortcut APIs are available on macOS, iOS, iPadOS, and
// tvOS—everywhere keyboard input is accepted.
#main
struct MessageApp : App {
var body: some Scene {
WindowGroup {
MessageView()
}
.commands {
MessageCommands()
}
}
}
struct MessageCommands : Commands {
// Try to observe a binding to the key window's `Message` model.
//
// In order for this to work, a view in the key window's focused view
// hierarchy (often the root view) needs to publish a binding using the
// `View.focusedValue(_:_:)` view modifier and the same `\.message` key
// path (anologous to a key path for an `Environment` value, defined
// below).
#FocusedBinding(\.message) var message: Message?
// FocusedBinding is a binding-specific convenience to provide direct
// access to a wrapped value.
//
// `FocusedValue` is a more general form of the property wrapper, designed
// to work with all value types, including bindings. The following is
// functionally equivalent, but places the burden of unwrapping the bound
// value on the client.
// #FocusedValue(\.message) var message: Binding<Message>?
var body: some Commands {
CommandMenu("Message") {
Button("Send", action: { message?.send() })
.keyboardShortcut("D") // Shift-Command-D
.disabled(message?.text.isEmpty ?? true)
}
}
}
struct MessageView : View {
#State var message = Message(text: "Hello, SwiftUI!")
var body: some View {
TextEditor(text: $message.text)
.focusedValue(\.message, $message)
.frame(idealWidth: 600, idealHeight: 400)
}
}
struct Message {
var text: String
// ...
mutating func send() {
print("Sending message: \(text)")
// ...
}
}
struct FocusedMessageKey : FocusedValueKey {
typealias Value = Binding<Message>
}
extension FocusedValues {
var message: FocusedMessageKey.Value? {
get { self[FocusedMessageKey.self] }
set { self[FocusedMessageKey.self] = newValue }
}
}
Some of the UI setups not working automatically with the Dark/Light mode change as the UIColor. For example shadow in layer. As I need to remove and drop shadow in dark and light mode, I need somewhere to put updateShadowIfNeeded() function. I know how to detect what is the mode currently:
func dropShadowIfNeeded() {
switch traitCollection.userInterfaceStyle {
case .dark: removeShadow()
case .light: dropShadowIfNotDroppedYet()
default: assertionFailure("Unknown userInterfaceStyle")
}
}
Now I put the function inside the layoutSubviews, since it gets called every time appearance change:
override func layoutSubviews() {
super.layoutSubviews()
dropShadowIfNeeded()
}
But this function is getting called A LOT. What is the proper function to trigger only if userInterfaceStyle changed?
SwiftUI
With a simple environment variable on the \.colorScheme key:
struct ContentView: View {
#Environment(\.colorScheme) var colorScheme
var body: some View {
Text(colorScheme == .dark ? "Its Dark" : "Its. not dark! (Light)")
}
}
UIKit
As it described in WWDC 2019 - Session 214 around 23:30.
As I expected, this function is getting called a lot including when colors changing. Along side with many other functions for ViewController and presentationController. But there is some especial function designed for that has a similar signature in all View representers.
Take a look at this image from that session:
Gray: Calling but not good for my issue, Green: Designed for this
So I should call it and check it inside this function:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
dropShadowIfNeeded()
}
}
This will guarantee to be called just once per change.
if you are only looking for the initial state of the style, check out this answer here
I think this should get called significantly less often, plus the guard makes sure you only react to user interface style changes:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else {
return
}
dropShadowIfNeeded()
}
With RxSwift and ObjectiveC runtime, you can achieve it without inheritance
here is the encapsulated version:
import UIKit
import RxSwift
import RxCocoa
enum SystemTheme {
static func get(on view: UIView) -> UIUserInterfaceStyle {
view.traitCollection.userInterfaceStyle
}
static func observe(on view: UIView) -> Observable<UIUserInterfaceStyle> {
view.rx.methodInvoked(#selector(UIView.traitCollectionDidChange(_:)))
.map { _ in SystemTheme.get(on: view) }
.distinctUntilChanged()
}
}
as we know, to implement PageTabBarController, we need to insert these code in AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
let viewControllers = [MatchDetailViewController(),ListPlayersViewController(),ChatViewController()]
window = UIWindow(frame: Device.bounds)
window!.rootViewController = MatchViewController(viewControllers: viewControllers, selectedIndex: 0)
window!.makeKeyAndVisible()
}
Now, i need to use PageTabBarController when i want to open detail for my match data. My question is, how to implement it without insert those code in AppDelegate.swift because it will open my MatchViewController (extend from PageTabBarController) for the first app launch.
I have tried this code, but it will cause Crash, and it pointed to my AppDelegate.swift
class MatchViewController: PageTabBarController {
var window: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
open override func prepare() {
super.prepare()
let viewControllers = [MatchDetailViewController(),ListPlayersViewController(),ChatViewController()]
//1st try: Crash
window = UIWindow(frame: Device.bounds)
window!.rootViewController = MatchViewController(viewControllers: viewControllers, selectedIndex: 0)
window!.makeKeyAndVisible()
//2nd try: error
self.rootViewController = MatchViewController(viewControllers: viewControllers, selectedIndex: 0)
//3rd try: crash
self.viewControllers = viewControllers
delegate = self
preparePageTabBar()
}
fileprivate func preparePageTabBar() {
pageTabBar.lineColor = Color.blue.base
pageTabBar.dividerColor = Color.blueGrey.lighten5
pageTabBarAlignment = PageTabBarAlignment.top
pageTabBar.lineAlignment = TabBarLineAlignment.bottom
}
}
extension MatchViewController: PageTabBarControllerDelegate {
func pageTabBarController(_ pageTabBarController: PageTabBarController, didTransitionTo viewController: UIViewController) {
}
}
Linked GitHub Question
Hi, yes there is a way. The PageTabBarController is inherited from aUIViewController`, which allows you to add it as a child of any other UIViewController. That said, you just gave me a great idea. I am going to make a new UIViewController that allows you to add as many child UIViewControllers, which will make this super easy to do. I will make this as a Feature Request.
Until the update, please use the suggested method of adding it as a child UIViewController. Are you familiar with how to do that?
First create AppToolbarController (subclass of ToolbarController) or you can use the one in the Material library demo.
And then from your view controller, you can use:
DispatchQueue.main.async {
let tabbarViewController = AppPageTabbarController(viewControllers: [vc1,vc2,vc3], selectedIndex: 0)
self.present(AppToolbarController(rootViewontroller: tabbarViewController))
}
Scenario:
I'm translating an Objective-C sample code supplied by Apple into Swift 3.0.1.
I came across some code that requires the need to prevent the presentationController from being released prior to calling preventViewController. Hence the use of NS_VALID_UNTIL_END_OF_SCOPE (see image below).
What's the best alternative using Swift?
...without it... all I get is a nil for the transitioningDelegate value upon access soon afterwards.
One thing you can try is using withExtendedLifetime(_:_:):
override func perform() {
let sourceViewController = self.destination
let destinationViewController = self.destination
// For presentations which will use a custom presentation controller,
// it is possible for that presentation controller to also be the
// transitioningDelegate.
//
// transitioningDelegate does not hold a strong reference to its
// destination object. To prevent presentationController from being
// released prior to calling -presentViewController:animated:completion:
// the NS_VALID_UNTIL_END_OF_SCOPE attribute is appended to the declaration.
let presentationController = AAPLAdaptivePresentationController(presentedViewController: destinationViewController, presenting: sourceViewController)
withExtendedLifetime(presentationController) {
destinationViewController.transitioningDelegate = presentationController
self.source.present(destinationViewController, animated: true, completion: nil)
}
}
Or else, this would work in the case shown in the picture:
override func perform() {
let sourceViewController = self.destination
let destinationViewController = self.destination
// For presentations which will use a custom presentation controller,
// it is possible for that presentation controller to also be the
// transitioningDelegate.
//
// transitioningDelegate does not hold a strong reference to its
// destination object. To prevent presentationController from being
// released prior to calling -presentViewController:animated:completion:
// the NS_VALID_UNTIL_END_OF_SCOPE attribute is appended to the declaration.
let presentationController = AAPLAdaptivePresentationController(presentedViewController: destinationViewController, presenting: sourceViewController)
destinationViewController.transitioningDelegate = presentationController
self.source.present(destinationViewController, animated: true, completion: {
let _ = presentationController //<- having a strong reference in the completion handler
})
}