in my app, I will add different themes, but for now, I want to write an extension that can handle keyboard appearance from extension.
for the first step, I just want to have control of its appearance, so I set that it should be .dark but it's not working. could you tell me where is the problem in this simple extension?
I just want it to change the keyboard appearance automatically without doing anything else
extension UITextView {
var keyboardApperance: UIKeyboardAppearance? {
get {
return self.keyboardAppearance
}
set {
self.keyboardAppearance = .dark
}
}
}
Creating just an extension doesn't change your UITextView properties.
You can create your own custom UITextView and use it instead of UITextView.
Don't forget to set your class if you are using storyboard or xib.
#IBDesignable
public class CustomDarkTextView: UITextView {
public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
setup()
}
public convenience init(frame: CGRect) {
self.init(frame: frame, textContainer: nil)
setup()
}
public convenience init() {
self.init(frame: CGRect.zero, textContainer: nil)
setup()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
self.keyboardAppearance = UIKeyboardAppearance.Dark
}
}
trying using functions like this
extension UITextView {
func setKeyboardToDark() {
self.keyboardAppearance = .dark
}
func setKeyboardToLight() {
self.keyboardAppearance = .light
}
}
extension UITextView {
func setDarkKeyboard() {
self.keyboardAppearance = UIKeyboardAppearance.dark
}
func setLightKeyboard() {
self.keyboardAppearance = UIKeyboardAppearance.light
}
}
Related
I am trying to create a custom UILabel where the text color would be red.
Here's what I tried and none of this works:
class CustomLabel: UILabel {
override func awakeFromNib() {
UILabel.appearance().textColor = UIColor.blue
textColor = UIColor.blue
}
}
You're missing another case, a Label can be created with and without nib. Try this:
class MyCustomLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// This will call `awakeFromNib` in your code
setup()
}
private func setup() {
self.textColor = .red
}
}
In the following code, I'm trying to pass an additional argument to VidePlayerView. Right now, I'm creating an instance of VideoPlayerView and I'm passing no arguments to it. However, this ends up calling the init methods with an NSRect arguments (which I don't really know where that comes from).
I want to pass an additional argument to VideoPlayerView, but I have no idea how to do this since it seems that I don't have access to the frame argument.
import SwiftUI
import AVKit
// Note: I couldn't find a way to pass this through the `init` method
var playerLayer = AVCaptureVideoPreviewLayer()
final class VideoPlayerView: NSView {
// MARK: - Initializers
override public init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
required public init?(coder decoder: NSCoder) {
super.init(coder: decoder)
commonInit()
}
private func commonInit() {
// Do something with playerLayer
}
}
public struct VideoPlayerViewView: NSViewRepresentable {
init(layer: AVCaptureVideoPreviewLayer?) {
if let layer = layer {
playerLayer = layer
} else {
print("No Layer Set")
}
}
public func makeNSView(context: NSViewRepresentableContext<VideoPlayerViewView>) -> NSView {
return VideoPlayerView()
}
}
Currently, I'm solving this problem by ignoring the frame argument, but not sure if that is important.
final class VideoPlayerView: NSView {
private var playerLayer = AVCaptureVideoPreviewLayer()
private let rootLayer = CALayer()
// MARK: - Initializers
override public init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
public init(layer: AVCaptureVideoPreviewLayer) {
self.playerLayer = layer
super.init(frame: NSRect())
commonInit()
}
...
public func makeNSView(context: NSViewRepresentableContext<VideoPlayerViewView>) -> NSView {
return VideoPlayerView(layer: self.playerLayer)
}
Here is possible approach. Tested with Xcode 11.4
final class VideoPlayerView: NSView {
private var playerLayer: AVCaptureVideoPreviewLayer?
// MARK: - Initializers
override public init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
public convenience init(frame frameRect: NSRect = .zero, layer: AVCaptureVideoPreviewLayer?) {
self.init(frame: frameRect)
self.playerLayer = layer
}
required public init?(coder decoder: NSCoder) {
super.init(coder: decoder)
commonInit()
}
private func commonInit() {
// Do something with playerLayer
}
}
public struct VideoPlayerViewView: NSViewRepresentable {
init(layer: AVCaptureVideoPreviewLayer?) {
if let layer = layer {
playerLayer = layer
} else {
print("No Layer Set")
}
}
public func makeNSView(context: NSViewRepresentableContext<VideoPlayerViewView>) -> NSView {
return VideoPlayerView(layer: playerLayer)
}
public func updateNSView(_ nsView: NSView, context: Context) {
}
}
I have problems in getting my custom NSSlider control updated live within Xcode's Interface Builder.
I have implemented #IBDesignable and prepareForInterfaceBuilder as shown in many other posts and tutorials. My little test just removes the knob from the slider control.
Here is the code I am using at the moment:
import Cocoa
#IBDesignable
class ColorSlider2: NSSlider {
override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect)
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
private func setupView() {
if ((self.cell?.isKind(of: ColorSlider2Cell.self)) == false) {
let cell = ColorSlider2Cell()
self.cell = cell
}
self.alphaValue = 0.5
self.floatValue = 0.4
}
}
class ColorSlider2Cell: NSSliderCell {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
override func drawKnob(_ knobRect: NSRect) {
return
}
}
The preview in Interface Builder is neither removing the knob nor updating the floatValue:
Do you have any idea why this is the case?
In Cocoa Touch, you can disable user interaction of a text view like this:
let textView = UITextView()
textView.isUserInteractionEnabled = false
But in Cocoa, NSTextView has no such easy API to use.
So what's the proper way to disable NSTextView user interaction?
You can disable its' editable by:
textView.isEditable = false
And its' selectable by:
textView.isSelectable = false
However, when you disable both of them. This textView still will intercept Mouse-down event, which is different from isUserInteractionEnabled .
So, for now, to enable click-through, my solution is subclassing a NSTextView and override its' hitTest:
class TextView: NSTextView {
override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
}
An encapsulated way is here
class StaticTextView: NSTextView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
}
I have this custom class for a button.
import UIKit
#IBDesignable
class CustomButton: UIButton {
#IBInspectable var cornerRadiusValue: CGFloat = 10.0 {
didSet {
setUpView()
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 10.0
}
override func awakeFromNib() {
setUpView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
}
func setUpView() {
self.layer.cornerRadius = 10.0
}
}
But the corner radius is not showing on the button in the storyboard.
I understand #IBInspectable just allows you to change the value in the inspector panel. I guess thats not what I am looking for.
I would like the corner radius to just show in storyboard when I create a button with that class. Which I thought that's what #IBDesignable does.
Your code is crashing in IB, so designability fails. Here is much simpler code that works:
#IBDesignable
class CustomButton: UIButton {
#IBInspectable var cornerRadiusValue: CGFloat = 10.0 {
didSet {
setUpView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
setUpView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
}
func setUpView() {
self.layer.cornerRadius = self.cornerRadiusValue
self.clipsToBounds = true
}
}
Now the button is both inspectable and designable:
And it also works in the running app.
Try this one:
#IBDesignable
class CustomButton: UIButton {
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
}
Here's a better solution than the accepted answer:
#IBDesignable
class DesignableButton: UIButton {
#IBInspectable private var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.masksToBounds = newValue > 0
layer.cornerRadius = newValue
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setNeedsDisplay()
}
}
It uses a computed property to avoid duplicating state with a stored property.