As title described,
my NSCollectionView is not working when single selection.
MyNSCollectionView is rendered correcttly,
below code shows how I initialize my NSCollectionView:
self.leftBar.dataSource = self
self.leftBar.delegate = self
self.leftBar.isSelectable = true
self.leftBar.allowsEmptySelection = true
let layout = NSCollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = NSSize(width: 200 , height: 50)
leftBar.collectionViewLayout = layout
self.contentView.addSubview(leftBar)
leftBar <- [
Top(),
Left(),
Bottom(),
Width(200)
]
Custom NSCollectionViewItem
class LeftBarCell: NSCollectionViewItem {
var leftBarView : LeftBarView?
override func loadView() {
leftBarView = LeftBarView(frame: NSZeroRect)
view = leftBarView!
}
func setup(title : String){
leftBarView?.titleTextView.string = title
}
}
In LeftBarView
class LeftBarView: NSView {
lazy var titleTextView : NSTextView = {
let titleTextView = NSTextView()
titleTextView.isEditable = false
titleTextView.isSelectable = false
titleTextView.font = NSFont(name: "Helvetica", size: 20)
return titleTextView
}()
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setupViews(){
self.addSubview(titleTextView)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.lightGray.cgColor
titleTextView <- Edges()
}
}
I tried to play around with
self.leftBar.isSelectable = true
leftBar.allowsMultipleSelection = true
and multiple selection is working.
The didSelectItemsAt from NSCollectionViewDelegate is triggered
But then when I try with this
self.leftBar.isSelectable = true
or
self.leftBar.isSelectable = true
self.leftBar.allowsEmptySelection = true
It is not working when I click on the collectionviewitem,
The didSelectItemsAt from NSCollectionViewDelegate is not triggered.
Any thoughts would be appreciated, thanks!
Solved. This is because the textview covers the cell, u might need to disable the textview to become passive.
extension NSTextView {
open override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
}
Related
Question
I've implemented a NSTextView SwiftUI-wrapper (following this great example). There are several of these NSTextViews on my view. In the app's menu, there is a button that should change the currently focused NSTextView's content, e.g:
Is there a way to determine which NSTextView is currently focused? In my current solution, I resorted to storing the NSTextView in a global view model's variable by passing the NSTextView when its "becomeFirstResponder" is called.
However, I'm afraid this solution could either lead to retain cycles or to the NSTextView stored in the view model becoming nil. Is there a cleaner way of doing this? Any help is appreciated!
Current solution/Code
NSTextView
struct TextArea: NSViewRepresentable {
// Source : https://stackoverflow.com/a/63761738/2624880
#Binding var text: NSAttributedString
#Binding var selectedRange: NSRange
#Binding var isFirstResponder: Bool
func makeNSView(context: Context) -> NSScrollView {
context.coordinator.createTextViewStack()
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
if let textArea = nsView.documentView as? NSTextView {
textArea.textStorage?.setAttributedString(self.text)
if !(self.selectedRange.location == textArea.selectedRange().location && self.selectedRange.length == textArea.selectedRange().length) {
textArea.setSelectedRange(self.selectedRange)
}
// Set focus (SwiftUI 👉 AppKit)
if isFirstResponder {
nsView.becomeFirstResponder()
DispatchQueue.main.async {
if ViewModel.shared.focusedTextView != textArea {
ViewModel.shared.focusedTextView = textArea
}
}
} else {
nsView.resignFirstResponder()
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text, selectedRange: $selectedRange, isFirstResponder: $isFirstResponder)
}
class Coordinator: NSObject, NSTextViewDelegate {
var text: Binding<NSAttributedString>
var selectedRange: Binding<NSRange>
var isFirstResponder: Binding<Bool>
init(text: Binding<NSAttributedString>,
selectedRange: Binding<NSRange>,
isFirstResponder: Binding<Bool>) {
self.text = text
self.selectedRange = selectedRange
self.isFirstResponder = isFirstResponder
}
func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementNSAttributedString text: NSAttributedString?) -> Bool {
defer {
self.text.wrappedValue = textView.attributedString()
self.selectedRange.wrappedValue = textView.selectedRange()
}
return true
}
fileprivate lazy var textStorage = NSTextStorage()
fileprivate lazy var layoutManager = NSLayoutManager()
fileprivate lazy var textContainer = NSTextContainer()
fileprivate lazy var textView: NSTextViewWithFocusHandler = NSTextViewWithFocusHandler(frame: CGRect(), textContainer: textContainer)
fileprivate lazy var scrollview = NSScrollView()
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
self.text.wrappedValue = NSAttributedString(attributedString: textView.attributedString())
self.selectedRange.wrappedValue = textView.selectedRange()
}
func textViewDidChangeSelection(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
DispatchQueue.main.async {
if !(self.selectedRange.wrappedValue.location == textView.selectedRange().location && self.selectedRange.wrappedValue.length == textView.selectedRange().length) {
self.selectedRange.wrappedValue = textView.selectedRange()
}
}
}
func textDidBeginEditing(_ notification: Notification) {
DispatchQueue.main.async {
self.isFirstResponder.wrappedValue = true
}
}
func textDidEndEditing(_ notification: Notification) {
DispatchQueue.main.async {
self.isFirstResponder.wrappedValue = false
}
}
func createTextViewStack() -> NSScrollView {
let contentSize = scrollview.contentSize
textContainer.containerSize = CGSize(width: contentSize.width, height: CGFloat.greatestFiniteMagnitude)
textContainer.widthTracksTextView = true
textView.minSize = CGSize(width: 0, height: 0)
textView.maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
textView.isVerticallyResizable = true
textView.frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
textView.autoresizingMask = [.width]
textView.delegate = self
scrollview.borderType = .noBorder
scrollview.hasVerticalScroller = true
scrollview.documentView = textView
scrollview.layer?.cornerRadius = 10
scrollview.drawsBackground = false
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
return scrollview
}
}
}
class NSTextViewWithFocusHandler: NSTextView {
override func becomeFirstResponder() -> Bool {
// ⚠️ Set self as currently focused TextView (AppKit 👉 SwiftUI)
ViewModel.shared.focusedTextView = self
return super.becomeFirstResponder()
}
}
ViewModel
class ViewModel: ObservableObject {
static let shared = ViewModel()
#Published var attributedTextQuestion: NSAttributedString = NSAttributedString(string: "Initial value question")
#Published var attributedTextAnswer: NSAttributedString = NSAttributedString(string: "Initial value answer")
#Published var selectedRangeQuestion: NSRange = NSRange()
#Published var selectedRangeAnswer: NSRange = NSRange()
#Published var questionFocused: Bool = false // Only works in direction SwiftUI 👉 AppKit
#Published var answerFocused: Bool = false // (dito)
weak var focusedTextView: NSTextView? {didSet{
DispatchQueue.main.async {
self.menuItemEnabled = self.focusedTextView != nil
}
}}
#Published var menuItemEnabled: Bool = false
}
ContentView
struct ContentView: View {
#ObservedObject var model: ViewModel
var body: some View {
VStack {
Text("Question")
TextArea(text: $model.attributedTextQuestion,
selectedRange: $model.selectedRangeQuestion,
isFirstResponder: $model.questionFocused)
Text("Answer")
TextArea(text: $model.attributedTextAnswer,
selectedRange: $model.selectedRangeAnswer,
isFirstResponder: $model.answerFocused)
}
.padding()
}
}
App
#main
struct TextViewMacOSSOFrageApp: App {
#ObservedObject var model: ViewModel = ViewModel.shared
var body: some Scene {
WindowGroup {
ContentView(model: model)
}.commands {
CommandGroup(replacing: .textFormatting) {
Button(action: {
// ⚠️ The currently focused TextView is retrieved and its AttributedString updated
guard let focusedTextView = model.focusedTextView else { return }
let newAttString = NSMutableAttributedString(string: "Value set through menu item")
newAttString.addAttribute(.backgroundColor, value: NSColor.yellow, range: NSRange(location: 0, length: 3))
focusedTextView.textStorage?.setAttributedString(newAttString)
focusedTextView.didChangeText()
}) {
Text("Insert image")
}.disabled(!model.menuItemEnabled)
}
}
}
}
I have a class for my buttons that changes the colour when pressed, alternating between on and off.
class KSPickButton: UIButton {
var isOn = true
override init(frame: CGRect) {
super.init(frame: frame)
initButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initButton()
}
func initButton() {
layer.borderWidth = 2.0
layer.borderColor = Colors.shanklinGreen.cgColor
layer.cornerRadius = frame.size.height/2
backgroundColor = .clear
setTitleColor(.white, for: .normal)
addTarget(self, action: #selector(KSPickButton.buttonPressed), for: .touchUpInside)
}
#objc func buttonPressed() {
activateButton(bool: !isOn)
}
func activateButton(bool: Bool) {
isOn = bool
let color = bool ? .clear : Colors.shanklinGreen
//let title = bool ? "" : ""
let titleColor = bool ? .white: Colors.shanklinBlack
//setTitle(title, for: .normal)
setTitleColor(titleColor, for: .normal)
backgroundColor = color
}
}
This works perfectly. I have 20 buttons on my main view controller and they flip between on and off as expected... Then maybe after pressing 6, I want to reset them all to off. I have a reset button on my main view controller, but I cannot work out how I can reset them all?
I can make them all look reset but the bool remains as was...
How do I call this class for all buttons and reset them correctly?
Introduced an observer with 'isOn' variable. You can try using following code snippet in 'KSPickButton' class. After that you need to take all subviews from view controllers (where buttons are placed) and set 'isOn' to 'false' for all of them.
class KSPickButton: UIButton {
public var isOn:Bool = true {
didSet {
handleButtonStateChange()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initButton()
}
func initButton() {
layer.borderWidth = 2.0
layer.borderColor = Colors.shanklinGreen.cgColor
layer.cornerRadius = frame.size.height/2
backgroundColor = .clear
setTitleColor(.white, for: .normal)
addTarget(self, action: #selector(KSPickButton.buttonPressed), for: .touchUpInside)
}
#objc func buttonPressed() {
isOn = !isOn
}
func handleButtonStateChange() {
let color = isOn ? .clear : Colors.shanklinGreen
let titleColor = isOn ? .white: Colors.shanklinBlack
setTitleColor(titleColor, for: .normal)
backgroundColor = color
}
}
Here is code snippet you need to implement in your view controller as explained above.
class MyVC: UIViewController {
------
------
for subview in view.subviews where subview.isKind(of: KSPickButton.self)&&(subview as? KSPickButton)?.isOn == true {
(subview as? KSPickButton)?.isOn = false
}
-----
-----
}
I have a NSSplitViewController with 2 view controllers as splitViewItems.
This works fine.
Now I would like to set an own SplitViewController Class for my Splitviewcontroller in the storyboard. I give it my own class start the app. But now I only see an empty window.
Have I set my splitviewcontroller now programmatically, if I set my own splitviewcontroller class?
If yes, which code I have to use to show the two view controllers in my splitview controller again?
UPDATE
import Cocoa
class SplitViewController: NSSplitViewController {
override func viewDidLoad() {
print("Test")
}
}
Here is an Xcode 9 Playground (Swift 4) which shows how to setup NSSplitViewController from code.
import Cocoa
import PlaygroundSupport
class ViewController: NSViewController {
private let backgroundColor: NSColor
init(backgroundColor: NSColor) {
self.backgroundColor = backgroundColor
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = NSView()
view.wantsLayer = true
view.layer?.backgroundColor = backgroundColor.cgColor
}
}
class MainSplitViewController: NSSplitViewController {
private let splitViewResorationIdentifier = "com.company.restorationId:mainSplitViewController"
lazy var vcA = ViewController(backgroundColor: .red)
lazy var vcB = ViewController(backgroundColor: .green)
lazy var vcC = ViewController(backgroundColor: .blue)
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setupUI()
setupLayout()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension MainSplitViewController {
private func setupUI() {
view.wantsLayer = true
splitView.dividerStyle = .paneSplitter
splitView.autosaveName = NSSplitView.AutosaveName(rawValue: splitViewResorationIdentifier)
splitView.identifier = NSUserInterfaceItemIdentifier(rawValue: splitViewResorationIdentifier)
}
private func setupLayout() {
minimumThicknessForInlineSidebars = 180
let itemA = NSSplitViewItem(sidebarWithViewController: vcA)
itemA.minimumThickness = 80
addSplitViewItem(itemA)
let itemB = NSSplitViewItem(contentListWithViewController: vcB)
itemB.minimumThickness = 100
addSplitViewItem(itemB)
let itemC = NSSplitViewItem(viewController: vcC)
itemC.minimumThickness = 80
addSplitViewItem(itemC)
}
}
let vc = MainSplitViewController()
vc.view.frame = CGRect(x: 0, y: 0, width: 400, height: 300)
PlaygroundPage.current.liveView = vc
UPDATE:
Version similar to one above but targeted to macOS 10.10.
import Cocoa
import PlaygroundSupport
class ViewController: NSViewController {
private let backgroundColor: NSColor
init(backgroundColor: NSColor) {
self.backgroundColor = backgroundColor
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = NSView()
view.wantsLayer = true
view.layer?.backgroundColor = backgroundColor.cgColor
}
}
class MainSplitViewController: NSSplitViewController {
private let splitViewResorationIdentifier = "com.company.restorationId:mainSplitViewController"
lazy var vcA = ViewController(backgroundColor: .red)
lazy var vcB = ViewController(backgroundColor: .green)
lazy var vcC = ViewController(backgroundColor: .blue)
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setupUI()
setupLayout()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension MainSplitViewController {
private func setupUI() {
view.wantsLayer = true
splitView.dividerStyle = .paneSplitter
splitView.autosaveName = NSSplitView.AutosaveName(rawValue: splitViewResorationIdentifier)
splitView.identifier = NSUserInterfaceItemIdentifier(rawValue: splitViewResorationIdentifier)
vcA.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
vcB.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
vcC.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
}
private func setupLayout() {
let sidebarItem = NSSplitViewItem(viewController: vcA)
sidebarItem.canCollapse = true
sidebarItem.holdingPriority = NSLayoutConstraint.Priority(NSLayoutConstraint.Priority.defaultLow.rawValue + 1)
addSplitViewItem(sidebarItem)
let xibItem = NSSplitViewItem(viewController: vcB)
addSplitViewItem(xibItem)
let codeItem = NSSplitViewItem(viewController: vcC)
addSplitViewItem(codeItem)
}
}
let vc = MainSplitViewController()
vc.view.frame = CGRect(x: 0, y: 0, width: 400, height: 300)
PlaygroundPage.current.liveView = vc
I've created a custom tab bar that is displaying tab bar items correctly. When I select a tab / icon the tab bar item's view controller is displayed but the icon does not change to the 'Selected image' icon i.e. the icons don't change when their view controller is being shown.
What am I doing wrong? How can I get the icons to update to the images that I've set on IB as the selected images?
Here is some of my code:
class CustomTabBarController: UITabBarController, CustomTabBarDataSource, CustomTabBarDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBar.isHidden = true
let customTabBar = CustomTabBar(frame: self.tabBar.frame)
customTabBar.datasource = self
customTabBar.delegate = self
customTabBar.setup()
self.view.addSubview(customTabBar)
}
// MARK: - CustomTabBarDataSource
func tabBarItemsInCustomTabBar(_ tabBarView: CustomTabBar) -> [UITabBarItem] {
return tabBar.items!
}
// MARK: - CustomTabBarDelegate
func didSelectViewController(_ tabBarView: CustomTabBar, atIndex index: Int) {
self.selectedIndex = index
}
}
class CustomTabBar: UIView {
var tabBarItems: [UITabBarItem]!
var customTabBarItems: [CustomTabBarItem]!
var tabBarButtons: [UIButton]!
func setup() {
tabBarItems = datasource.tabBarItemsInCustomTabBar(self)
customTabBarItems = []
tabBarButtons = []
let containers = createTabBarItemContainers()
createTabBarItems(containers)
}
func createTabBarItems(_ containers: [CGRect]) {
var index = 0
for item in tabBarItems {
let container = containers[index]
let customTabBarItem = CustomTabBarItem(frame: container)
customTabBarItem.setup(item)
self.addSubview(customTabBarItem)
customTabBarItems.append(customTabBarItem)
let button = UIButton(frame: CGRect(x: 0, y: 0, width: container.width, height: container.height))
button.addTarget(self, action: #selector(CustomTabBar.barItemTapped(_:)), for: UIControlEvents.touchUpInside)
customTabBarItem.addSubview(button)
tabBarButtons.append(button)
index += 1
}
}
func barItemTapped(_ sender : UIButton) {
let index = tabBarButtons.index(of: sender)!
delegate.didSelectViewController(self, atIndex: index)
}
Change
class CustomTabBar: UIView {
to:
class CustomTabBar: UITabBar {
Then your custom tab bar will act like a tab bar!
Well I had a same kind of functionality and implemented with UITabBarController like this.
enum TabType:Int{
case viewController1 = 0
case viewController2 = 1
case viewController3 = 2
}
class CustomTabbarVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
convenience init(userType : UserType){
self.init()
addViewControllers()
setupOnInit()
let tabBar = self.tabBar
tabBar.selectionIndicatorImage = UIImage().createSelectionIndicator(UIColor(red: 22/255, green: 52/255, blue: 89/255, alpha: 1.0), size: CGSize(width: tabBar.frame.width/CGFloat(tabBar.items!.count), height: tabBar.frame.height), lineWidth: 3.0)
}
func setupOnInit(){
delegate = self
tabBar.barStyle = UIBarStyle.black
tabBar.isTranslucent = false
}
func addViewControllers(){
// We will add 3 controllers
let viewController1 = viewController1(nibName: “viewController1”, bundle: nil)
let viewController2 = viewController2(nibName: “viewController2”, bundle: nil)
let viewController3 = viewController3(nibName: “viewController3”, bundle: nil)
let viewController1Navigation = UINavigationController(rootViewController: viewController1)
viewController1Navigation.tabBarItem = getTabbarItem(.viewController1)
viewController1Navigation.tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)
let viewController2Navigation = UINavigationController(rootViewController: viewController2)
viewController2Navigation.tabBarItem = getTabbarItem(.viewController2)
viewController2Navigation.tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)
let viewController3Navigation = UINavigationController(rootViewController: viewController3)
viewController3Navigation.tabBarItem = getTabbarItem(.viewController3)
viewController3Navigation.tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)
viewControllers = [viewController1Navigation,viewController2Navigation,viewController3Navigation]
}
func getTabbarItem(_ tabType:TabType)->UITabBarItem{
// Fetch tab bar item and set image according to it.
var image = String()
var selectedImage = String()
if tabType == .viewController1{
image = “img_viewController1_tab_nonSelected”
selectedImage = “img_viewController1_tab_Selected”
}else if tabType == .viewController2{
image = “img_viewController2_tab_nonSelected”
selectedImage = “img_viewController2_tab_Selected”
}else if tabType == .viewController3{
image = “img_viewController3_tab_nonSelected”
selectedImage = “img_viewController3_tab_Selected”
}else{
print("Unknown tab type")
}
if let imageName:String = image,let selectedImageName:String = selectedImage{
return UITabBarItem(title: nil, image: UIImage(named: imageName)?.withRenderingMode(.alwaysOriginal), selectedImage: UIImage(named: selectedImageName)?.withRenderingMode(.alwaysOriginal))
}else{
return UITabBarItem()
}
}
}
extension UIImage {
func createSelectionIndicator(_ color: UIColor, size: CGSize, lineWidth: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(CGRect(x: 0, y: size.height - lineWidth, width: size.width, height: lineWidth))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
How to implement in App delegate
class AppDelegate: UIResponder, UIApplicationDelegate{
var window: UIWindow?
var customTabbarVC: CustomTabbarVC?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
customTabbarVC = customTabbarVC() // It will invoke init methods of customtabbarvc
window?.rootViewController = customTabbarVC // Here your tab bar controller will setup
return true
}
// Other APP DELEGATE METHODS
}
Let me know if you have any questions..
For Changing image in your custom tab bar button after you click the button you need to write the code to change image in below function
func barItemTapped(_ sender : UIButton) {
}
similarly
func barItemTapped(_ sender : UIButton)
{
if sender.tag == 1
{
tabBarButtons.setImage(UIImage(named:"FirstImage.png"), forState: .Normal)
}
else
{
tabBarButtons.setImage(UIImage(named:"SecImage.png"), forState: .Normal)
}
}
I want to set my Switch like this:
But I try in ios9 , it does not work.
I saw in apple UISwitch Class Reference.
It says that :
Discussion
In iOS 7, this property has no effect.
How about iOS 9? Any one success?
My Code:
switch1 = UISwitch(frame:CGRectMake(self.view.frame.width/2 - 20, 400, 10, 100))
switch1.on = true
switch1.onTintColor = UIColor.lightGrayColor()
switch1.tintColor = UIColor.greenColor()
switch1.thumbTintColor = UIColor.blackColor()
//set on/off image
switch1.onImage = UIImage(named: "on-switch")
switch1.offImage = UIImage(named: "off-switch")
Use a UIButton instead.
let switchButton = UIButton(type: .Custom)
switchButton.selected = true
switchButton.setImage(UIImage(named: "on-switch"), forState: .Selected)
switchButton.setImage(UIImage(named: "off-switch"), forState: .Normal)
Use switchButton.isSelected instead of switch1.on. You'll have to toggle switchButton.isSelected when it is tapped, which you can do like this:
switchButton.isSelected.toggle()
For iOS 13, you could do this way:
let switcher = UISwitch()
switcher.addTarget(self, action: #selector(pressed), for: .valueChanged)
#objc func pressed(sender: UISwitch) {
let color = UIColor(patternImage: UIImage(named: sender.isOn ? "on.png": "off.png")!)
if sender.isOn {
sender.onTintColor = color
} else {
sender.tintColor = color
sender.subviews[0].subviews[0].backgroundColor = color
}
}
NOTE: your image should look like:
Then the final result is:
Not an exact answer to your question, but if you want a completely custom button switch programmatically (that you can add text to), this will work too:
import UIKit
class RDHiddenVisibleButton: UIButton {
// Hidden / Visible Button Function
var isOn = false
override init(frame: CGRect) {
super.init(frame: frame)
initButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initButton()
}
func initButton() {
layer.borderWidth = 2.0
layer.borderColor = Colors.radiusGreen.cgColor
layer.cornerRadius = frame.size.height/2
setTitleColor(Colors.radiusGreen, for: .normal)
addTarget(self, action: #selector(RDHiddenVisibleButton.buttonPressed), for: .touchUpInside)
}
#objc func buttonPressed() {
activateButton(bool: !isOn)
}
func activateButton(bool: Bool) {
isOn = bool
let color = bool ? Colors.radiusGreen : .clear
let title = bool ? "Hidden" : "Visible"
let titleColor = bool ? . white : Colors.radiusGreen
setTitle(title, for: .normal)
setTitleColor(titleColor, for: .normal)
backgroundColor = color
}
}