How to Highlight and Clickable if Text is URL SwiftUI - swift

Is there any way to Highlight the Text And clickable if Text contain URL in the chat screen from both
Sender USer Code:-
import SwiftUI
struct TextHighlightURL: View {
var body: some View {
HStack(alignment: .bottom){
Spacer()
Text("2:22 PM").font(.system(size: 10))
.foregroundColor(.gray)
HStack {
Text("www.google.com")
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.padding(10)
}
.background(Color.init("DTR-ChatSendrColor"))
.cornerRadius(10.0)
}.padding(.vertical,5)
.padding()
}
}
Output:-
Reciver User Code:-
struct SenderReciverUI1: View {
var body: some View {
Group {
HStack(alignment: .bottom){
VStack(alignment: .leading,spacing:5) {
HStack(alignment: .bottom) {
Text("www.google.com")
.foregroundColor(.white)
.padding(10)
.cornerRadius(10.0)
}
}
Spacer()
}.padding(.vertical,5)
.padding()
}
}
}
My Goal:-
Can someone please explain to me how to show Highlight the Text from both side if sender send the link and receiver receive the link it automatically highlighted And clickable if Text contain URL, I've tried to implement by above but no results yet.
Any help would be greatly appreciated.
Thanks in advance.

You need to create custom UIViewRepresentable for TextView.
check below code this might help you.
struct TextView: UIViewRepresentable {
#Binding var text: String
#Binding var textStyle: UIFont.TextStyle
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.delegate = context.coordinator
textView.font = UIFont.preferredFont(forTextStyle: textStyle)
textView.autocapitalizationType = .sentences
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.isEditable = false
textView.dataDetectorTypes = .link
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
uiView.font = UIFont.preferredFont(forTextStyle: textStyle)
}
func makeCoordinator() -> Coordinator {
Coordinator($text)
}
class Coordinator: NSObject, UITextViewDelegate {
var text: Binding<String>
init(_ text: Binding<String>) {
self.text = text
}
func textViewDidChange(_ textView: UITextView) {
self.text.wrappedValue = textView.text
}
}
}
In your "Receiver User Code:-" same for "Sender User Code"
struct SenderReciverUI1: View {
#State private var message = "Hello, www.google.com. this is just testing for hyperlinks, check this out our website https://www.apple.in thank you."
#State private var textStyle = UIFont.TextStyle.body
var body: some View {
Group {
HStack(alignment: .bottom){
VStack(alignment: .leading,spacing:5) {
HStack(alignment: .bottom) {
TextView(text: $message, textStyle: $textStyle)
.foregroundColor(.white)
.padding(10)
.cornerRadius(10.0)
}
}
Spacer()
}.padding(.vertical,5)
.padding()
}
}
}
let me know if you need anything.

Related

How can I make a toolbar with page indicators in SwiftUI like the Weather App?

In SwiftUI, I am trying to place page Indicators on top of a bottom toolbar, but have not come to a resolution.
Paging Indicators
Right now, I have a tabview that organizes Views 1-7 horizontally, but the page indicators are on its own island at the bottom of the screen:
TabView {
View1()
View2()
View3()
View4()
View5()
View6()
View7()
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
I am trying to place the indicators on top of a toolbar with other buttons like how the Apple Weather App has done it:
Apple Weather App
I have also tried using a NavigationView with the .toolbar(ToolbarItemGroup) modifier, but that has not worked for me either.
Please let me know if you can help me with this.
Thanks
You can do this by wrapping a UIPageControl in a UIViewRepresentable, and then overlay that over your TabView using a ZStack or a .overlay modifier. You'll want to use .tabViewStyle(.page(indexDisplayMode: .never)) to prevent the tab view from displaying its own page control.
Here's a wrapper for UIPageControl.
struct PageControl: UIViewRepresentable {
#Binding var currentPage: Int
var numberOfPages: Int
func makeCoordinator() -> Coordinator {
return Coordinator(currentPage: $currentPage)
}
func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.numberOfPages = 1
control.setIndicatorImage(UIImage(systemName: "location.fill"), forPage: 0)
control.pageIndicatorTintColor = UIColor(.primary)
control.currentPageIndicatorTintColor = UIColor(.accentColor)
control.translatesAutoresizingMaskIntoConstraints = false
control.setContentHuggingPriority(.required, for: .horizontal)
control.addTarget(
context.coordinator,
action: #selector(Coordinator.pageControlDidFire(_:)),
for: .valueChanged)
return control
}
func updateUIView(_ control: UIPageControl, context: Context) {
context.coordinator.currentPage = $currentPage
control.numberOfPages = numberOfPages
control.currentPage = currentPage
}
class Coordinator {
var currentPage: Binding<Int>
init(currentPage: Binding<Int>) {
self.currentPage = currentPage
}
#objc
func pageControlDidFire(_ control: UIPageControl) {
currentPage.wrappedValue = control.currentPage
}
}
}
And here's an example of how to use it:
struct ContentView: View {
#State var page = 0
var locations = ["Current Location", "San Francisco", "Chicago", "New York", "London"]
var body: some View {
ZStack(alignment: .bottom) {
tabView
VStack {
Spacer()
controlBar.padding()
Spacer().frame(height: 60)
}
}
}
#ViewBuilder
private var tabView: some View {
TabView(selection: $page) {
ForEach(locations.indices, id: \.self) { i in
WeatherPage(location: locations[i])
.tag(i)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
#ViewBuilder
private var controlBar: some View {
HStack {
Image(systemName: "map")
Spacer()
PageControl(
currentPage: $page,
numberOfPages: locations.count
)
Spacer()
Image(systemName: "list.bullet")
}
}
}
struct WeatherPage: View {
var location: String
var body: some View {
VStack {
Spacer()
Text("Weather in \(location)")
Spacer()
}
}
}

UISwitch.appearance().onTintColor not working since iOS 14 [duplicate]

I've implemented a toggle after following Apple's tutorial on user input. Currently, it looks like this:
This is the code that produces this UI:
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
}
}
Now, I'd like the Toggle's on-color to be blue instead of green.
I tried:
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
.accentColor(.blue)
.foregroundColor(.blue)
.background(Color.blue)
None of these worked and I wasn't able to find any other modifiers, such as tintColor.
How do I change the color of a Toggle?
SwiftUI 3.0
Using tint
A new modifier was introduced that can also change the Toggle color:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.tint(.red)
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.tint(.orange)
SwiftUI 2.0
Using SwitchToggleStyle
You can now set a tint color for the on position only in SwiftUI 2.0:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.red))
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.orange))
SwiftUI 1.0
Using ToggleStyle
I created a new ToggleStyle to change the three colors of the Toggle (on color, off color, and the thumb).
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = Color(UIColor.green)
var offColor = Color(UIColor.systemGray5)
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
Button(action: { configuration.isOn.toggle() } )
{
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
}
}
.font(.title)
.padding(.horizontal)
}
}
Examples of Use
Toggle("", isOn: $toggleState)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .green,
offColor: .red,
thumbColor: Color(UIColor.systemTeal)))
Toggle("", isOn: $toggleState2)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .purple))
From the SwiftUI Book
Just use UIAppearance APIs:
UISwitch.appearance().onTintColor = UIColor.blue
It'll of course by default change the appearance of all the instances of UISwitch, as per UIAppearance documentation.
NOTE: Tested as of Xcode 11 beta 5.
SwiftUI 2.0 (Post WWDC-2020)
Using the new SwiftUI enhancements you can use the .toggleStyle modifier.
// Switch tinting
Toggle(isOn: $order.notifyWhenReady) {
Text("Send notification when ready")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Note this only works for iOS14/iPadOS14/macOS11 and above.
I haven't found a way to directly change a Toggle color yet but an alternative way to have a blue switch or any other custom views, is to create a custom view of your own. To make a custom blue toggle in its simplest form:
struct BlueToggle : UIViewRepresentable {
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.onTintColor = UIColor.blue
}
}
struct ContentView : View {
var body: some View {
BlueToggle()
}
}
Result:
You can modify the global onTintColor for all UISwitch objects inside init().
#State var enable_dhcp = true
init()
{
UISwitch.appearance().onTintColor = .red
}
var body: some View
{
Toggle("DHCP", isOn: $enable_dhcp)
}
Building off #mohammad-reza-farahani 's solution, here is a fully uncompromising approach to getting the configurability of UISwitch with the implementation protocols if SwiftUI.
First wrap a UISwitch in a UIViewRepresentable and set the colors as you wish:
final class CustomToggleWrapper: UIViewRepresentable {
var isOn: Binding<Bool>
init(isOn: Binding<Bool>) {
self.isOn = isOn
}
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
// On color
uiView.onTintColor = UIColor.blue
// Off color
uiView.tintColor = UIColor.red
uiView.layer.cornerRadius = uiView.frame.height / 2
uiView.backgroundColor = UIColor.red
uiView.isOn = isOn.wrappedValue
// Update bound boolean
uiView.addTarget(self, action: #selector(switchIsChanged(_:)), for: .valueChanged)
}
#objc
func switchIsChanged(_ sender: UISwitch) {
isOn.wrappedValue = sender.isOn
}
}
Second, create a custom toggle style using the wrapped UISwitch:
struct CustomToggleStyle: ToggleStyle {
func makeBody(configuration: Self.Configuration) -> some View {
let toggle = CustomToggleWrapper(isOn: configuration.$isOn)
return HStack {
configuration.label
Spacer()
toggle
}
}
}
Implement a Toggle as you normally would, and apply your CustomToggleStyle:
struct TestView: View {
#State private var isOn: Bool = true
var body: some View {
Toggle(
isOn: $isOn
) {
Text("Test: \(String(isOn))")
}.toggleStyle(CustomToggleStyle()).padding()
}
}
Karol Kulesza and George Valkov have provided a very easy to implement solution. I just wanted to add that you can place the code below inside the app delegate's didFinishLaunching method as well.
UISwitch.appearance().onTintColor = .blue
You can also create more specific appearance configurations with
appearance(whenContainedInInstancesOf:)
See https://www.hackingwithswift.com/example-code/uikit/what-is-the-uiappearance-proxy
As the original question was just about changing the toggle on colour and not full Toggle visual customisation, I think something like this would do:
import SwiftUI
struct CustomToggle: UIViewRepresentable {
#Binding var isOn: Bool
func makeCoordinator() -> CustomToggle.Coordinator {
Coordinator(isOn: $isOn)
}
func makeUIView(context: Context) -> UISwitch {
let view = UISwitch()
view.onTintColor = UIColor.red
view.addTarget(context.coordinator, action: #selector(Coordinator.switchIsChanged(_:)), for: .valueChanged)
return view
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
class Coordinator: NSObject {
#Binding private var isOn: Bool
init(isOn: Binding<Bool>) {
_isOn = isOn
}
#objc func switchIsChanged(_ sender: UISwitch) {
_isOn.wrappedValue = sender.isOn
}
}
}
// MARK: - Previews
struct CustomToggle_Previews: PreviewProvider {
static var previews: some View {
ViewWrapper()
}
struct ViewWrapper: View {
#State(initialValue: false) var isOn: Bool
var body: some View {
CustomToggle(isOn: $isOn)
.previewLayout(.fixed(width: 100, height: 100))
}
}
}
The easist way is setting UISwitch.appearance().onTintColor = UIColor.red before using toggle and use SwiftUI Toggle like below.
UISwitch.appearance().onTintColor = UIColor.red
...
let toggle = Toggle(isOn: $vm.dataUsePermission, label: {
Text(I18N.permit_data_usage)
.font(SwiftUI.Font.system(size: 16, weight: .regular))
})
if #available(iOS 14.0, *) {
toggle.toggleStyle(
SwitchToggleStyle(tint: Color(UIColor.m.blue500))
)
} else {
toggle.toggleStyle(SwitchToggleStyle())
}
...
You can alse use same Toggle interface in SwiftUI but different name, and change tint color.
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
If only need Toggle without Label, then
TintableUISwitch(isOn: .constant(true))
Use below code.
import SwiftUI
public struct TintableSwitch<Label>: View where Label: View {
#Binding var isOn: Bool
var label: Label
public init(isOn: Binding<Bool>, #ViewBuilder label: () -> Label) {
self._isOn = isOn
self.label = label()
}
public var body: some View {
HStack {
label
Spacer()
TintableUISwitch(isOn: $isOn, onTintColor: .red) // 📌 CHANGE HERE
}
}
}
public struct TintableUISwitch: UIViewRepresentable {
#Binding var isOn: Bool
private var onTintColor: UIColor
public init(isOn: Binding<Bool>, onTintColor: UIColor = UIColor.m.blue500) {
self._isOn = isOn
self.onTintColor = onTintColor
}
public func makeUIView(context: Context) -> UISwitch {
let uiSwitch = UISwitch()
uiSwitch.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
uiSwitch.onTintColor = onTintColor
uiSwitch.isOn = isOn
return uiSwitch
}
public func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public class Coordinator: NSObject {
var tintableSwitch: TintableUISwitch
init(_ tintableSwitch: TintableUISwitch) {
self.tintableSwitch = tintableSwitch
}
#objc
func valueChanged(_ sender: UISwitch) {
tintableSwitch.isOn = sender.isOn
}
}
}
struct TintableSwitch_Previews: PreviewProvider {
static var previews: some View {
VStack {
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
}
}
}
struct TintableUISwitch_Previews: PreviewProvider {
static var previews: some View {
TintableUISwitch(isOn: .constant(true))
}
}
You can change the toggle color in IOS 15.0 using a tint modifier.
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.tint(.red)
and below IOS 15.0, You can use toggleStyle modifier to change the toggle color but it will be depreciated in the future.
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.toggleStyle(SwitchToggleStyle(tint: .red))
I would change #Mark Moeykens answer a little bit to avoid having the button tap animation. A better solution would be:
#available(iOS 13.0, *)
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = UIColor.proacPrimaryBlue.suColor
var offColor = UIColor.systemGray5.suColor
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
.onTapGesture {
configuration.isOn.toggle()
}
}
.font(.title)
.padding(.horizontal)
}
}
This https://stackoverflow.com/a/56480720/5941807 (for now whit Xcode 11 beta 6) is a solution. To switch between to option a fast way is using the boolean instead of if/else:
showFavoritesOnly ? .red : .blue
for foreground:
Toggle(isOn: $showGreeting) {
Text("Show Favorites only").foregroundColor(showFavoritesOnly ? .blue : .gray)
}
for tint:
uiView.onTintColor = showFavoritesOnly ? UIColor.blue : UIColor.gray
In addition for custom colors: https://stackoverflow.com/a/57744208/5941807

Change background color of TextEditor in SwiftUI

TextEditor seems to have a default white background. So the following is not working and it displayed as white instead of defined red:
var body: some View {
TextEditor(text: .constant("Placeholder"))
.background(Color.red)
}
Is it possible to change the color to a custom one?
iOS 16
You should hide the default background to see your desired one:
TextEditor(text: .constant("Placeholder"))
.scrollContentBackground(.hidden) // <- Hide it
.background(.red) // To see this
iOS 15 and below
TextEditor is backed by UITextView. So you need to get rid of the UITextView's backgroundColor first and then you can set any View to the background.
struct ContentView: View {
init() {
UITextView.appearance().backgroundColor = .clear
}
var body: some View {
List {
TextEditor(text: .constant("Placeholder"))
.background(.red)
}
}
}
Demo
You can find my simple trick for growing TextEditor here in this answer
Pure SwiftUI solution on iOS and macOS
colorMultiply is your friend.
struct ContentView: View {
#State private var editingText: String = ""
var body: some View {
TextEditor(text: $editingText)
.frame(width: 400, height: 100, alignment: .center)
.cornerRadius(3.0)
.colorMultiply(.gray)
}
}
Update iOS 16 / SwiftUI 4.0
You need to use .scrollContentBackground(.hidden) instead of UITextView.appearance().backgroundColor = .clear
https://twitter.com/StuFFmc/status/1556561422431174656
Warning: This is an iOS 16 only so you'll probably need some if #available and potentially two different TextEditor component.
extension View {
/// Layers the given views behind this ``TextEditor``.
func textEditorBackground<V>(#ViewBuilder _ content: () -> V) -> some View where V : View {
self
.onAppear {
UITextView.appearance().backgroundColor = .clear
}
.background(content())
}
}
Custom Background color with SwiftUI on macOS
On macOS, unfortunately, you have to fallback to AppKit and wrap NSTextView.
You need to declare a view that conforms to NSViewRepresentable
This should give you pretty much the same behaviour as SwiftUI's TextEditor-View and since the wrapped NSTextView does not draw its background, you can use the .background-ViewModifier to change the background
struct CustomizableTextEditor: View {
#Binding var text: String
var body: some View {
GeometryReader { geometry in
NSScrollableTextViewRepresentable(text: $text, size: geometry.size)
}
}
}
struct NSScrollableTextViewRepresentable: NSViewRepresentable {
typealias Representable = Self
// Hook this binding up with the parent View
#Binding var text: String
var size: CGSize
// Get the UndoManager
#Environment(\.undoManager) var undoManger
// create an NSTextView
func makeNSView(context: Context) -> NSScrollView {
// create NSTextView inside NSScrollView
let scrollView = NSTextView.scrollableTextView()
let nsTextView = scrollView.documentView as! NSTextView
// use SwiftUI Coordinator as the delegate
nsTextView.delegate = context.coordinator
// set drawsBackground to false (=> clear Background)
// use .background-modifier later with SwiftUI-View
nsTextView.drawsBackground = false
// allow undo/redo
nsTextView.allowsUndo = true
return scrollView
}
func updateNSView(_ scrollView: NSScrollView, context: Context) {
// get wrapped nsTextView
guard let nsTextView = scrollView.documentView as? NSTextView else {
return
}
// fill entire given size
nsTextView.minSize = size
// set NSTextView string from SwiftUI-Binding
nsTextView.string = text
}
// Create Coordinator for this View
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// Declare nested Coordinator class which conforms to NSTextViewDelegate
class Coordinator: NSObject, NSTextViewDelegate {
var parent: Representable // store reference to parent
init(_ textEditor: Representable) {
self.parent = textEditor
}
// delegate method to retrieve changed text
func textDidChange(_ notification: Notification) {
// check that Notification.name is of expected notification
// cast Notification.object as NSTextView
guard notification.name == NSText.didChangeNotification,
let nsTextView = notification.object as? NSTextView else {
return
}
// set SwiftUI-Binding
parent.text = nsTextView.string
}
// Pass SwiftUI UndoManager to NSTextView
func undoManager(for view: NSTextView) -> UndoManager? {
parent.undoManger
}
// feel free to implement more delegate methods...
}
}
Usage
ContenView: View {
#State private var text: String
var body: some View {
VStack {
Text("Enter your text here:")
CustomizableTextEditor(text: $text)
.background(Color.red)
}
.frame(minWidth: 600, minHeight: 400)
}
}
Edit:
Pass reference to SwiftUI UndoManager so that default undo/redo actions are available.
Wrap NSTextView in NSScrollView so that it is scrollable. Set minSize property of NSTextView to enclosing SwiftUIView-Size so that it fills the entire allowed space.
Caveat: Only first line of this custom TextEditor is clickable to enable text editing.
This works for me on macOS
extension NSTextView {
open override var frame: CGRect {
didSet {
backgroundColor = .clear
drawsBackground = true
}
}
}
struct ContentView: View {
#State var text = ""
var body: some View {
TextEditor(text: $text)
.background(Color.red)
}
Reference this answer
To achieve this visual design here is the code I used.
iOS 16
TextField(
"free_form",
text: $comment,
prompt: Text("Type your feedback..."),
axis: .vertical
)
.lineSpacing(10.0)
.lineLimit(10...)
.padding(16)
.background(Color.themeSeashell)
.cornerRadius(16)
iOS 15
ZStack(alignment: .topLeading) {
RoundedRectangle(cornerRadius: 16)
.foregroundColor(.gray)
TextEditor(text: $comment)
.padding()
.focused($isFocused)
if !isFocused {
Text("Type your feedback...")
.padding()
}
}
.frame(height: 132)
.onAppear() {
UITextView.appearance().backgroundColor = .clear
}
You can use Mojtaba's answer (the approved answer). It works in most cases. However, if you run into this error:
"Return from initializer without initializing all stored properties"
when trying to use the init{ ... } method, try adding UITextView.appearance().backgroundColor = .clear to .onAppear{ ... } instead.
Example:
var body: some View {
VStack(alignment: .leading) {
...
}
.onAppear {
UITextView.appearance().backgroundColor = .clear
}
}
Using the Introspect library, you can use .introspectTextView for changing the background color.
TextEditor(text: .constant("Placeholder"))
.cornerRadius(8)
.frame(height: 100)
.introspectTextView { textView in
textView.backgroundColor = UIColor(Color.red)
}
Result
import SwiftUI
struct AddCommentView: View {
init() {
UITextView.appearance().backgroundColor = .clear
}
var body: some View {
VStack {
if #available(iOS 16.0, *) {
TextEditor(text: $viewModel.commentText)
.scrollContentBackground(.hidden)
} else {
TextEditor(text: $viewModel.commentText)
}
}
.background(Color.blue)
.frame(height: UIScreen.main.bounds.width / 2)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.red, lineWidth: 1)
)
}
}
It appears the UITextView.appearance().backgroundColor = .clear trick in IOS 16,
only works for the first time you open the view and the effect disappear when the second time it loads.
So we need to provide both ways in the app. Answer from StuFF mc works.
var body: some View {
if #available(iOS 16.0, *) {
mainView.scrollContentBackground(.hidden)
} else {
mainView.onAppear {
UITextView.appearance().backgroundColor = .clear
}
}
}
// rename body to mainView
var mainView: some View {
TextEditor(text: $notes).background(Color.red)
}

How could I add 'skyfloatLabelText' in my swift UI view?

I would like to use skyfloatingLabelText in swift UI. Their example show like below
let textField = SkyFloatingLabelTextField(frame: CGRect(x: 10, y: 10, width: 200, height: 45))
textField.placeholder = "Name"
textField.title = "Your full name"
self.view.addSubview(textField)
I have no idea how I could implement the above code in my swift UI. Below are my swift UI.
import SwiftUI
import SkyFloatingLabelTextField
struct Login: View {
var body: some View {
VStack() {
HeaderBar()
Spacer()
HStack {
VStack {
Text("Login Page")
.fontWeight(.bold)
TextField("Name", text: Value) //default input is ok
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Login")
}
}
}
Spacer()
}
}
}
struct Login_Previews: PreviewProvider {
static var previews: some View {
Login()
}
}
Thanks in advance.
Like #Asperi mentioned, you need to be using UIViewRepresentable for this.
The delegate example is taken from the github example.
import SwiftUI
import SkyFloatingLabelTextField
struct SkyFloatingContentView: UIViewRepresentable {
class Coordinator: NSObject, UITextFieldDelegate {
var parent: SkyFloatingContentView
init(_ parent: SkyFloatingContentView) {
self.parent = parent
}
func textFieldDidChangeSelection(_ textField: UITextField) {
if let text = textField.text {
if let floatingLabelTextField = textField as? SkyFloatingLabelTextField {
if (text.count < 3 || !text.contains("#")) {
floatingLabelTextField.errorMessage = "Invalid email"
} else {
// The error message will only disappear when we reset it to nil or empty string
floatingLabelTextField.errorMessage = ""
}
}
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> SkyFloatingLabelTextField {
return SkyFloatingLabelTextField(frame: CGRect(x: 10, y: 10, width: 200, height: 45))
}
func updateUIView(_ textField: SkyFloatingLabelTextField, context: Context) {
textField.placeholder = "Name"
textField.title = "Your full name"
textField.delegate = context.coordinator
}
}
struct ContentView: View {
var body: some View {
VStack() {
Spacer()
HStack {
VStack {
Text("Login Page")
.fontWeight(.bold)
SkyFloatingContentView()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Login")
}
}
}
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Set Toggle color in SwiftUI

I've implemented a toggle after following Apple's tutorial on user input. Currently, it looks like this:
This is the code that produces this UI:
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
}
}
Now, I'd like the Toggle's on-color to be blue instead of green.
I tried:
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
.accentColor(.blue)
.foregroundColor(.blue)
.background(Color.blue)
None of these worked and I wasn't able to find any other modifiers, such as tintColor.
How do I change the color of a Toggle?
SwiftUI 3.0
Using tint
A new modifier was introduced that can also change the Toggle color:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.tint(.red)
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.tint(.orange)
SwiftUI 2.0
Using SwitchToggleStyle
You can now set a tint color for the on position only in SwiftUI 2.0:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.red))
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.orange))
SwiftUI 1.0
Using ToggleStyle
I created a new ToggleStyle to change the three colors of the Toggle (on color, off color, and the thumb).
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = Color(UIColor.green)
var offColor = Color(UIColor.systemGray5)
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
Button(action: { configuration.isOn.toggle() } )
{
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
}
}
.font(.title)
.padding(.horizontal)
}
}
Examples of Use
Toggle("", isOn: $toggleState)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .green,
offColor: .red,
thumbColor: Color(UIColor.systemTeal)))
Toggle("", isOn: $toggleState2)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .purple))
From the SwiftUI Book
Just use UIAppearance APIs:
UISwitch.appearance().onTintColor = UIColor.blue
It'll of course by default change the appearance of all the instances of UISwitch, as per UIAppearance documentation.
NOTE: Tested as of Xcode 11 beta 5.
SwiftUI 2.0 (Post WWDC-2020)
Using the new SwiftUI enhancements you can use the .toggleStyle modifier.
// Switch tinting
Toggle(isOn: $order.notifyWhenReady) {
Text("Send notification when ready")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Note this only works for iOS14/iPadOS14/macOS11 and above.
I haven't found a way to directly change a Toggle color yet but an alternative way to have a blue switch or any other custom views, is to create a custom view of your own. To make a custom blue toggle in its simplest form:
struct BlueToggle : UIViewRepresentable {
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.onTintColor = UIColor.blue
}
}
struct ContentView : View {
var body: some View {
BlueToggle()
}
}
Result:
You can modify the global onTintColor for all UISwitch objects inside init().
#State var enable_dhcp = true
init()
{
UISwitch.appearance().onTintColor = .red
}
var body: some View
{
Toggle("DHCP", isOn: $enable_dhcp)
}
Building off #mohammad-reza-farahani 's solution, here is a fully uncompromising approach to getting the configurability of UISwitch with the implementation protocols if SwiftUI.
First wrap a UISwitch in a UIViewRepresentable and set the colors as you wish:
final class CustomToggleWrapper: UIViewRepresentable {
var isOn: Binding<Bool>
init(isOn: Binding<Bool>) {
self.isOn = isOn
}
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
// On color
uiView.onTintColor = UIColor.blue
// Off color
uiView.tintColor = UIColor.red
uiView.layer.cornerRadius = uiView.frame.height / 2
uiView.backgroundColor = UIColor.red
uiView.isOn = isOn.wrappedValue
// Update bound boolean
uiView.addTarget(self, action: #selector(switchIsChanged(_:)), for: .valueChanged)
}
#objc
func switchIsChanged(_ sender: UISwitch) {
isOn.wrappedValue = sender.isOn
}
}
Second, create a custom toggle style using the wrapped UISwitch:
struct CustomToggleStyle: ToggleStyle {
func makeBody(configuration: Self.Configuration) -> some View {
let toggle = CustomToggleWrapper(isOn: configuration.$isOn)
return HStack {
configuration.label
Spacer()
toggle
}
}
}
Implement a Toggle as you normally would, and apply your CustomToggleStyle:
struct TestView: View {
#State private var isOn: Bool = true
var body: some View {
Toggle(
isOn: $isOn
) {
Text("Test: \(String(isOn))")
}.toggleStyle(CustomToggleStyle()).padding()
}
}
Karol Kulesza and George Valkov have provided a very easy to implement solution. I just wanted to add that you can place the code below inside the app delegate's didFinishLaunching method as well.
UISwitch.appearance().onTintColor = .blue
You can also create more specific appearance configurations with
appearance(whenContainedInInstancesOf:)
See https://www.hackingwithswift.com/example-code/uikit/what-is-the-uiappearance-proxy
As the original question was just about changing the toggle on colour and not full Toggle visual customisation, I think something like this would do:
import SwiftUI
struct CustomToggle: UIViewRepresentable {
#Binding var isOn: Bool
func makeCoordinator() -> CustomToggle.Coordinator {
Coordinator(isOn: $isOn)
}
func makeUIView(context: Context) -> UISwitch {
let view = UISwitch()
view.onTintColor = UIColor.red
view.addTarget(context.coordinator, action: #selector(Coordinator.switchIsChanged(_:)), for: .valueChanged)
return view
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
class Coordinator: NSObject {
#Binding private var isOn: Bool
init(isOn: Binding<Bool>) {
_isOn = isOn
}
#objc func switchIsChanged(_ sender: UISwitch) {
_isOn.wrappedValue = sender.isOn
}
}
}
// MARK: - Previews
struct CustomToggle_Previews: PreviewProvider {
static var previews: some View {
ViewWrapper()
}
struct ViewWrapper: View {
#State(initialValue: false) var isOn: Bool
var body: some View {
CustomToggle(isOn: $isOn)
.previewLayout(.fixed(width: 100, height: 100))
}
}
}
The easist way is setting UISwitch.appearance().onTintColor = UIColor.red before using toggle and use SwiftUI Toggle like below.
UISwitch.appearance().onTintColor = UIColor.red
...
let toggle = Toggle(isOn: $vm.dataUsePermission, label: {
Text(I18N.permit_data_usage)
.font(SwiftUI.Font.system(size: 16, weight: .regular))
})
if #available(iOS 14.0, *) {
toggle.toggleStyle(
SwitchToggleStyle(tint: Color(UIColor.m.blue500))
)
} else {
toggle.toggleStyle(SwitchToggleStyle())
}
...
You can alse use same Toggle interface in SwiftUI but different name, and change tint color.
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
If only need Toggle without Label, then
TintableUISwitch(isOn: .constant(true))
Use below code.
import SwiftUI
public struct TintableSwitch<Label>: View where Label: View {
#Binding var isOn: Bool
var label: Label
public init(isOn: Binding<Bool>, #ViewBuilder label: () -> Label) {
self._isOn = isOn
self.label = label()
}
public var body: some View {
HStack {
label
Spacer()
TintableUISwitch(isOn: $isOn, onTintColor: .red) // 📌 CHANGE HERE
}
}
}
public struct TintableUISwitch: UIViewRepresentable {
#Binding var isOn: Bool
private var onTintColor: UIColor
public init(isOn: Binding<Bool>, onTintColor: UIColor = UIColor.m.blue500) {
self._isOn = isOn
self.onTintColor = onTintColor
}
public func makeUIView(context: Context) -> UISwitch {
let uiSwitch = UISwitch()
uiSwitch.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
uiSwitch.onTintColor = onTintColor
uiSwitch.isOn = isOn
return uiSwitch
}
public func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public class Coordinator: NSObject {
var tintableSwitch: TintableUISwitch
init(_ tintableSwitch: TintableUISwitch) {
self.tintableSwitch = tintableSwitch
}
#objc
func valueChanged(_ sender: UISwitch) {
tintableSwitch.isOn = sender.isOn
}
}
}
struct TintableSwitch_Previews: PreviewProvider {
static var previews: some View {
VStack {
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
}
}
}
struct TintableUISwitch_Previews: PreviewProvider {
static var previews: some View {
TintableUISwitch(isOn: .constant(true))
}
}
You can change the toggle color in IOS 15.0 using a tint modifier.
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.tint(.red)
and below IOS 15.0, You can use toggleStyle modifier to change the toggle color but it will be depreciated in the future.
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.toggleStyle(SwitchToggleStyle(tint: .red))
I would change #Mark Moeykens answer a little bit to avoid having the button tap animation. A better solution would be:
#available(iOS 13.0, *)
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = UIColor.proacPrimaryBlue.suColor
var offColor = UIColor.systemGray5.suColor
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
.onTapGesture {
configuration.isOn.toggle()
}
}
.font(.title)
.padding(.horizontal)
}
}
This https://stackoverflow.com/a/56480720/5941807 (for now whit Xcode 11 beta 6) is a solution. To switch between to option a fast way is using the boolean instead of if/else:
showFavoritesOnly ? .red : .blue
for foreground:
Toggle(isOn: $showGreeting) {
Text("Show Favorites only").foregroundColor(showFavoritesOnly ? .blue : .gray)
}
for tint:
uiView.onTintColor = showFavoritesOnly ? UIColor.blue : UIColor.gray
In addition for custom colors: https://stackoverflow.com/a/57744208/5941807