When I create multiple radio buttons through a for loop and using the function addTarget, how can I handle multiple radio buttons using addTarget by tag or sender in swift 3.0?
Here is the solution change it according to your requirement
(SWIFT 4)
// create button array to save all button
var buttonArray = [UIButton]()
func createButtons(){
//create 5 buttons
for i in 1...5 {
let button = UIButton(frame: CGRect(x: 0, y: i*55, width: 130, height: 50))
//set same background for each button
button.backgroundColor = UIColor.darkGray
// set different ittle for button
button.setTitle("Button No \(i)", for: .normal)
// set different tag to differentiate betwwen button
button.tag = i
// set same target for each button
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
buttonArray.append(button)
//add all buttons to main view
view.addSubview(button)
}
// we make first button of different color so that it can act as radio button
let firstBtn = buttonArray[0]
firstBtn.backgroundColor = UIColor.green
}
#objc func buttonTapped(_ tappedBtn : UIButton){
// here we get tapped button so from array of button we match the selected one using tag and make it of different color all all other buttons of some different color
for button in buttonArray {
if(button.tag == tappedBtn.tag){
button.backgroundColor = UIColor.green
}else{
button.backgroundColor = UIColor.darkGray
}
}
}
Related
Im going through SwiftUI Controls in detail and writing example code for each init.
Theres two sets of inits for Picker.
One where you fill your list from an array etc. and what you pick goes into selection: binding.
But whats the second set of inits for?
Theyre under section 'Creating a picker for a collection'
I can fill the array from a collection using both these inits ok
But how do you get the selected item using the 2nd set of inits?
The selection: param is no longer a binding to an ivar but a Keypath to fill the list.
My question is how do I get the selected item using the 2nd set of inits.
See inits here:
https://developer.apple.com/documentation/swiftui/picker
For Picker there are 6 inits.
3 under 'Creating a picker'
these are ok. I fill the list from an array for example and store the selected item in a single result specified by the selection: param. It binds the result to one ivar.
There are also 3 inits under 'Creating a picker for a collection'
I got this to display the items from a collection
e.g. I modified the example code in the apple docs. The code in the docs doesn't compile so apple may be missing stuff.
import SwiftUI
enum Thickness: String, CaseIterable, Identifiable {
case thin
case regular
case thick
var id: String { rawValue }
}
//to use in ist must be Hashable
struct Border: Identifiable {
var color: Color
var thickness: Thickness
//Identifiable > Hashable > id > String
//var id: String { return "\(color.hashValue)" }
let id = UUID()
}
extension Color{
func colorName() -> String{
if self == Color.black{
return "black"
}
else if self == Color.red{
return "red"
}
else{
return "UNHANDLED"
}
}
}
struct CLCPickers_selection_FromCollection_View: View {
#State private var selectedObjectBorders = [
Border(color: .black, thickness: .thin),
Border(color: .red, thickness: .thick)
]
var body: some View {
VStack{
//------------------------------------------------------------------
Picker(
"Border Thickness",
sources: $selectedObjectBorders,
selection: \.thickness
) {
//------------------------------------------------------------------
//I added
//id: \.self
//Picker: the selection "thin" is invalid and does not have an associated tag, this will give undefined results.
//------------------------------------------------------------------
ForEach(Thickness.allCases,
id: \.self)
{ thickness in
Text(thickness.rawValue)
}
}
//------------------------------------------------------------------
Divider()
//------------------------------------------------------------------
//This just lists the colors in the arrays of Border
//QUESTION - how do I find out the currenly selected one?
//normaly selection: in the picker would be bound to the picked item
//but for this init selection: is a keypath
//selection: \.thickness
//so I can fill the Picker list using the keypath into the Border array.
//BUT HOW DO I FIND OUT THE CURRENTLY SELECTED ITEM?
//theres no binding?
//is there a .selectedItem property some where?
List(selectedObjectBorders) {
Text("\($0.color.colorName())")
}
}
}
}
Question was answered but poster removed it for some reason.
answer: this picker init which set the thinkness ivar of EVERY Border object in the collection.
To see the change I should have displayed the result to show thickness.rawvalue to see the change in every Border object
List(selectedObjectBorders) { border in
HStack{
Text("\(border.color.colorName())")
Text("\(border.thickness.rawValue)") //<<- will change when you select an item. All will match.
}
}
This example is pretty contrived, but it illustrates the behavior. I know you can use .accessibilityIdentifier to uniquely identify a control, but I'm just trying to better understand the interplay between XCUIElement and XCUIElementQuery.
Let's say you have an app like this:
import SwiftUI
struct ContentView: View {
#State var showRedButton = true
var body: some View {
VStack {
if showRedButton {
Button("Click me") {
showRedButton = false
}
.background(.red)
}
else {
HStack {
Button("Click me") {
showRedButton = true
}
.background(.blue)
Spacer()
}
}
}
}
}
And you are UI testing like this:
import XCTest
final class MyAppUITests: XCTestCase {
func testExample() throws {
let app = XCUIApplication()
app.launch()
print(app.debugDescription)
// At this point, the Element subtree shows a single Button:
// Button, 0x14e40d290, {{162.3, 418.3}, {65.3, 20.3}}, label: 'Click me'
let btn = app.buttons["Click me"]
btn.tap() // <-- This tap makes the red button disappear and shows the blue button
print(app.debugDescription)
// Now, the Element subtree shows a single Button that has a different ID
// and different x-y coordinates:
// Button, 0x15dc12e50, {{0.0, 418.3}, {65.3, 20.3}}, label: 'Click me'
btn.tap() // <-- This tap now works on the blue button?? Without requerying?
print(app.debugDescription)
// The red button reappears, but with a different ID (which makes sense).
}
}
Why does the second tap work, even though it's a different control? This must mean that SwiftUI is automatically re-running the XCUIElementQuery to find the button that matches "Click me". Apparently the variable btn isn't linked to the control with the ID 0x14e40d290. Does this mean XCUIElement actually represents an XCUIElementQuery? I expected it to require me to explicitly re-run the query like this,
btn = app.buttons["Click me"]
prior to running the 2nd tap, or the tap would've said that btn was no longer available.
The final print of the Element subtree shows that the red button has a different ID now. This makes sense, because when SwiftUI redraws the red button, it's not the same instance as the last time. This is explained well in the WWDC videos. Nevertheless, at the moment I connected the variable "btn" to the control, I thought there was a tighter affiliation. Maybe UI testing has to behave this way because SwiftUI redraws controls so frequently?
I am trying to get NSFontPanel/NSFontManager to work in a SwiftUI Document Template app. I have the following which is a customize version of one I found on GitHub. This lets me pick the size, face, style, etc.
Interestingly, a color picker is included in the FontPanel. The documentation doesn't seem to say this. Is this something new?
Anyway, I would like to either be able to use the color picker to let the user select a color, or if not I would like to hide the color picker - at is not "critical" to this application. I am using this to allow customization of text in a sidebar, so color is nice, but not necessary. Currently the Font settings are working, but the color selection displays, and let you pick on, but it always returns System Color.
Any help would be appreciated.
NOTE: I didn't include the FontPickerDelegate, it just calls this:
public struct FontPicker: View{
let labelString: String
#Binding var font: NSFont
#State var fontPickerDelegate: FontPickerDelegate?
public init(_ label: String, selection: Binding<NSFont>) {
self.labelString = label
self._font = selection
}
let fontManager = NSFontManager.shared
let fontPanel = NSFontPanel.shared
#AppStorage("setSidebarFont") var setSidebarFont = "System"
#AppStorage("setSidebarFontSize") var setSidebarFontSize = 24
#AppStorage("setSidebarFontColor") var setSidebarFontColor = "gray"
public var body: some View {
HStack {
Text(labelString)
Button {
if fontPanel.isVisible {
fontPanel.orderOut(nil)
return
}
self.fontPickerDelegate = FontPickerDelegate(self)
fontManager.target = self.fontPickerDelegate
fontManager.action = #selector(fontPickerDelegate?.changeAttributes)
fontPanel.setPanelFont(self.font, isMultiple: false)
fontPanel.orderBack(nil)
} label: {
Text("Font Selection: \(setSidebarFont)")
.font(.custom(setSidebarFont, size: CGFloat(setSidebarFontSize)))
}
}
}
func fontSelected() {
self.font = fontPanel.convert(self.font)
setSidebarFont = self.font.displayName ?? "System"
setSidebarFontSize = Int(self.font.pointSize)
var newAttributes = fontManager.convertAttributes([String : AnyObject]())
newAttributes["NSForegroundColorAttributeName"] = newAttributes["NSColor"]
newAttributes["NSUnderlineStyleAttributeName"] = newAttributes["NSUnderline"]
newAttributes["NSStrikethroughStyleAttributeName"] = newAttributes["NSStrikethrough"]
newAttributes["NSUnderlineColorAttributeName"] = newAttributes["NSUnderlineColor"]
newAttributes["NSStrikethroughColorAttributeName"] = newAttributes["NSStrikethroughColor"]
print("\(newAttributes["NSForegroundColorAttributeName"]!)")
}
}
I have a Mac OS menu bar which when clicked opens a popup view, I want to add upon this by incorporating a quit function. When the icon is right clicked another popup menu will be presented.
I am stuck on trying to get the another popup menu to present, I am able to detect a right click on the icon but I unable to show the popup menu view.
Preview when i left click
POPUP MENU VIEW FOR BOTH POPUPS
#main
struct MenuNoteApp: App {
let persistenceController = PersistenceController.shared
#Environment(\.scenePhase) var scenePhase
#NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
.onChange(of: scenePhase) { _ in
persistenceController.save()
}
}
}
extension NSStatusBarButton {
open override func rightMouseUp(with event: NSEvent) {
// Detect right click and prints right click
print("right click")
}
}
class AppDelegate: NSObject,NSApplicationDelegate{
let persistenceController = PersistenceController.shared
// Status Item
var statusItem: NSStatusItem?
// PopOver
var popOver = NSPopover()
var popOverOptions = NSPopover()
func applicationDidFinishLaunching( _ notification: Notification){
// Menu View
let menuView = MenuView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
// Creating PopOver
popOver.behavior = .transient
popOver.animates = true
// Setting Empty View Controller And Setting View as SwiftUI View with help of hosting controller
popOver.contentViewController = NSViewController()
popOver.contentViewController?.view = NSHostingView(rootView: menuView)
popOverOptions.contentViewController = NSViewController()
popOverOptions.contentViewController?.view = NSHostingView(rootView: OptionsView())
// Creating Status Bar Button
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
// Check if status button is available
if let MenuButton = statusItem?.button {
MenuButton.image = NSImage(systemSymbolName: "note", accessibilityDescription: nil)
MenuButton.action = #selector(MenuButtonToggle)
}
}
// Button Action
#objc func MenuButtonToggle(sender: AnyObject){
if popOver.isShown{
popOver.performClose(sender)
}
else if popOverOptions.isShown{
popOverOptions.performClose(sender)
}
else{
//Present Pop Over
if let menuButton = statusItem?.button{
// popOver is the main view while popOverOpitions is the second view
self.popOver.show(relativeTo: menuButton.bounds, of: menuButton, preferredEdge: NSRectEdge.minY)
}
}
}
}
Instead of overriding functions in NSStatusBarButton, you should detect the left and right click behavior within the MenuButtonToggle function.
First, you should register the right-click action, inside your the if-let inside applicationDidFinishLaunching, like so:
if let menuButton = statusItem?.button {
menuButton.image = NSImage(systemSymbolName: "note", accessibilityDescription: nil)
menuButton.action = #selector(MenuButtonToggle)
menuButton.sendAction(on: [.leftMouseUp, .rightMouseUp]) // register action on right click too
}
Then, you want to update the MenuButtonToggle function to act differently depending on left and right clicks.
#objc func menuButtonToggle(sender: AnyObject){
// ... previous logic here
if let event = NSApp.currentEvent {
if event.type == NSEventType.rightMouseUp {
// Right button click
self.popOver.show(...)
} else {
// Left button click
self.popOverOptions.show(...)
}
}
}
TvOS 13. I have a UITabBarController with tabs. And can customize almost everything except this obvious thing: focused tab's background. It's always white.
Guide tells
Specify tints for selected and unselected items
I tried:
view.backgroundColor = .purple
tabBar.tintColor = .yellow
tabBar.barTintColor = .red
tabBar.unselectedItemTintColor = .brown
tabBar.backgroundColor = .green
tabBar.backgroundImage = UIColor.blue.toImage()
tabBar.shadowImage = UIColor.orange.toImage()
tabBar.selectionIndicatorImage = UIColor.burgundy.toImage()
Nothing helped.
After playing a bit with various properties of UITabBar and UITabBarController, I finally figured it out.
The property to change focused items background color is selectionIndicatorTintColor of UITabBarAppearance (documentation).
Since it is available on tvOS >= 13.0, you will have to wrap the assignment like this:
if #available(tvOS 13.0, *) {
tabBar.standardAppearance.selectionIndicatorTintColor = .white
}
For #davidv and other folks, here is my solution:
extension UIView {
func subviews<T:UIView>(ofType type: T.Type) -> [T] {
var result = self.subviews.compactMap { $0 as? T }
for sub in self.subviews {
result.append(contentsOf: sub.subviews(ofType: type))
}
return result
}
}
extension UIViewController {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// перекраска кнопки
let allSubviews = tabBar.subviews(ofType: UIView.self)
let whiteSubviews = allSubviews.filter { $0.backgroundColor == .white }
for s in whiteSubviews {
s.backgroundColor = .gold
}
}
}
UPDATE:
For coloring text:
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorSelected], for: [.focused])
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorSelected], for: [.highlighted])
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorUnselected], for: [.normal])
For coloring background:
tabBar.standardAppearance.selectionIndicatorTintColor = .gold
I accomplish this through a UITabBar extension. The view that is displayed on focus contains a UIMotionEffect so we check against that to find it.
#available(tvOS 13.0, *)
extension UITabBar {
var focusBackgroundView: UIView? {
let allSubviews: [UIView] = subviews.flatMap { [$0] + $0.subviews as [UIView] }
return allSubviews.first{ !$0.motionEffects.isEmpty }
}
}
Usage:
myTabBar.focusBackgroundView.backgroundColor = .red