Ios TextField(“,text:.常量(“测试SwiftUI TextField”)) .背景(颜色(红色:0.5,绿色:0.5,蓝色:1)) //此视图的行为类似于“SelectableText”,但布局的行为类似于“TextField”` CustomTextField(文本:。常量(“测试`CustomTextField`”) .isEditable(可编辑:false) .背景(颜色.绿色) //一个不可选择的普通文本` 文本(“测试SwiftUI`Text`”) .背景(颜色.红色) //可选择的“文本”,通过下面的按钮可以更改选择功能 SelectableText(“测试`SelectableText`可能可选”) .可选择(自选择文本) .背景(颜色.橙色) 按钮(操作:{ self.selectableText.toggle() }) { 文本(“`SelectableText`可以选择:\(self.SelectableText.description)”) } //无法更改的可选“文本” SelectableText(“测试`SelectableText`始终可选”) .背景(颜色.黄色) }.padding() } } 让viewController=UIHostingController(rootView:TextTestView()) viewController.view.frame=CGRect(x:0,y:0,宽度:400,高度:200) PlaygroundPage.current.liveView=viewController.view
我遇到了类似的问题,本质上我希望在不允许编辑的情况下选择文本。在我的例子中,我希望在点击文本时显示UIMenuController,而不允许编辑文本或显示光标或键盘。基于先前的答案:Ios TextField(“,text:.常量(“测试SwiftUI TextField”)) .背景(颜色(红色:0.5,绿色:0.5,蓝色:1)) //此视图的行为类似于“SelectableText”,但布局的行为类似于“TextField”` CustomTextField(文本:。常量(“测试`CustomTextField`”) .isEditable(可编辑:false) .背景(颜色.绿色) //一个不可选择的普通文本` 文本(“测试SwiftUI`Text`”) .背景(颜色.红色) //可选择的“文本”,通过下面的按钮可以更改选择功能 SelectableText(“测试`SelectableText`可能可选”) .可选择(自选择文本) .背景(颜色.橙色) 按钮(操作:{ self.selectableText.toggle() }) { 文本(“`SelectableText`可以选择:\(self.SelectableText.description)”) } //无法更改的可选“文本” SelectableText(“测试`SelectableText`始终可选”) .背景(颜色.黄色) }.padding() } } 让viewController=UIHostingController(rootView:TextTestView()) viewController.view.frame=CGRect(x:0,y:0,宽度:400,高度:200) PlaygroundPage.current.liveView=viewController.view,ios,swift,text,swiftui,Ios,Swift,Text,Swiftui,我遇到了类似的问题,本质上我希望在不允许编辑的情况下选择文本。在我的例子中,我希望在点击文本时显示UIMenuController,而不允许编辑文本或显示光标或键盘。基于先前的答案: import SwiftUI import UIKit struct SelectableText: UIViewRepresentable { var text: String @Binding var isSelected: Bool func makeUIView(context
import SwiftUI
import UIKit
struct SelectableText: UIViewRepresentable {
var text: String
@Binding var isSelected: Bool
func makeUIView(context: Context) -> SelectableLabel {
let label = SelectableLabel()
label.textColor = .white
label.font = .systemFont(ofSize: 60, weight: .light)
label.minimumScaleFactor = 0.6
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .right
label.numberOfLines = 1
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
label.text = text
return label
}
func updateUIView(_ uiView: SelectableLabel, context: Context) {
uiView.text = text
if isSelected {
uiView.showMenu()
} else {
let _ = uiView.resignFirstResponder()
}
}
}
class SelectableLabel: UILabel {
override var canBecomeFirstResponder: Bool {
return true
}
override init(frame: CGRect) {
super.init(frame: .zero)
highlightedTextColor = .gray
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(copy(_:)), #selector(paste(_:)), #selector(delete(_:)):
return true
default:
return super.canPerformAction(action, withSender: sender)
}
}
override func copy(_ sender: Any?) {
UIPasteboard.general.string = self.stringValue
}
override func paste(_ sender: Any?) {
guard let string = UIPasteboard.general.string else { return }
NotificationCenter.default.post(name: Notification.Name.Paste, object: nil, userInfo: [Keys.PastedString: string])
}
override func delete(_ sender: Any?) {
NotificationCenter.default.post(name: Notification.Name.Delete, object: nil)
}
override func resignFirstResponder() -> Bool {
isHighlighted = false
return super.resignFirstResponder()
}
public func showMenu() {
becomeFirstResponder()
isHighlighted = true
let menu = UIMenuController.shared
menu.showMenu(from: self, rect: bounds)
}
}
我使用自定义粘贴和删除通知向我的模型对象发送消息,其中粘贴和删除操作被处理以适当地更新显示,这符合我的目的。也可以使用绑定
使用:
SelectableText(text: text, isSelected: self.$isSelected)
.onTapGesture {
self.isSelected.toggle()
}
.onReceive(NotificationCenter.default.publisher(for: UIMenuController.willHideMenuNotification)) { _ in
self.isSelected = false
}
或者,当我们想在不允许编辑的情况下显示文本的“复制”工具提示时,也可以使用类似的方法 作为好处,我们可以使用本机视图“文本”,这使我们有机会使用本机方法“.font()”、“.foregroundColor()”等。此外,我们还可以将其用于视图组,例如示例单元格 以先前的答案为基础 操场代码
import PlaygroundSupport
import SwiftUI
/// This subclass is needed since we want to customize the cursor and the context menu
class CustomUITextField: UITextField, UITextFieldDelegate {
/// Binding from the `CustomTextField` so changes of the text can be observed by `SwiftUI`
fileprivate var _textBinding: Binding<String>!
/// If it is `true` the text field behaves normally.
/// If it is `false` the text cannot be modified only selected, copied and so on.
fileprivate var _isEditable = true {
didSet {
// set the input view so the keyboard does not show up if it is edited
self.inputView = self._isEditable ? nil : UIView()
// do not show autocorrection if it is not editable
self.autocorrectionType = self._isEditable ? .default : .no
}
}
// change the cursor to have zero size
override func caretRect(for position: UITextPosition) -> CGRect {
return self._isEditable ? super.caretRect(for: position) : .zero
}
// override this method to customize the displayed items of 'UIMenuController' (the context menu when selecting text)
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
// disable 'cut', 'delete', 'paste','_promptForReplace:'
// if it is not editable
if (!_isEditable) {
switch action {
case #selector(cut(_:)),
#selector(delete(_:)),
#selector(paste(_:)):
return false
default:
// do not show 'Replace...' which can also replace text
// Note: This selector is private and may change
if (action == Selector("_promptForReplace:")) {
return false
}
}
}
return super.canPerformAction(action, withSender: sender)
}
// === UITextFieldDelegate methods
func textFieldDidChangeSelection(_ textField: UITextField) {
// update the text of the binding
self._textBinding.wrappedValue = textField.text ?? ""
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Allow changing the text depending on `self._isEditable`
return self._isEditable
}
}
struct CustomTextField: UIViewRepresentable {
@Binding private var text: String
private var isEditable: Bool
init(text: Binding<String>, isEditable: Bool = true) {
self._text = text
self.isEditable = isEditable
}
func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> CustomUITextField {
let textField = CustomUITextField(frame: .zero)
textField.delegate = textField
textField.text = self.text
textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
return textField
}
func updateUIView(_ uiView: CustomUITextField, context: UIViewRepresentableContext<CustomTextField>) {
uiView.text = self.text
uiView._textBinding = self.$text
uiView._isEditable = self.isEditable
}
func isEditable(editable: Bool) -> CustomTextField {
return CustomTextField(text: self.$text, isEditable: editable)
}
}
struct SelectableText: UIViewRepresentable {
private var text: String
private var selectable: Bool
init(_ text: String, selectable: Bool = true) {
self.text = text
self.selectable = selectable
}
func makeUIView(context: Context) -> CustomUITextField {
let textField = CustomUITextField(frame: .zero)
textField.delegate = textField
textField.text = self.text
textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
textField.setContentHuggingPriority(.defaultHigh, for: .horizontal)
return textField
}
func updateUIView(_ uiView: CustomUITextField, context: Context) {
uiView.text = self.text
uiView._textBinding = .constant(self.text)
uiView._isEditable = false
uiView.isEnabled = self.selectable
}
func selectable(_ selectable: Bool) -> SelectableText {
return SelectableText(self.text, selectable: selectable)
}
}
struct TextTestView: View {
@State private var selectableText = true
var body: some View {
VStack {
// Even though the text should be constant, it is not because the user can select and e.g. 'cut' the text
TextField("", text: .constant("Test SwiftUI TextField"))
.background(Color(red: 0.5, green: 0.5, blue: 1))
// This view behaves like the `SelectableText` however the layout behaves like a `TextField`
CustomTextField(text: .constant("Test `CustomTextField`"))
.isEditable(editable: false)
.background(Color.green)
// A non selectable normal `Text`
Text("Test SwiftUI `Text`")
.background(Color.red)
// A selectable `text` where the selection ability can be changed by the button below
SelectableText("Test `SelectableText` maybe selectable")
.selectable(self.selectableText)
.background(Color.orange)
Button(action: {
self.selectableText.toggle()
}) {
Text("`SelectableText` can be selected: \(self.selectableText.description)")
}
// A selectable `text` which cannot be changed
SelectableText("Test `SelectableText` always selectable")
.background(Color.yellow)
}.padding()
}
}
let viewController = UIHostingController(rootView: TextTestView())
viewController.view.frame = CGRect(x: 0, y: 0, width: 400, height: 200)
PlaygroundPage.current.liveView = viewController.view
import PlaygroundSupport
import SwiftUI
private class SelectableUIView: UIView {
var text: String?
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
self.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)))
}
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.showMenu(from: self, rect: frame)
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = text
UIMenuController.shared.hideMenu()
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy)
}
}
struct SelectableView: UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> UIView {
let view = SelectableUIView()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
guard let view = uiView as? SelectableUIView else {
return
}
view.text = text
}
}
struct SelectableContainer<Content: View>: View {
private let content: () -> Content
private var text: String
public init(text: String, @ViewBuilder content: @escaping () -> Content) {
self.text = text
self.content = content
}
public var body: some View {
ZStack {
content()
SelectableView(text: text)
.layoutPriority(-1)
}
}
}
struct SelectableText: View {
private var text: String
public init(_ text: String) {
self.text = text
}
public var body: some View {
ZStack {
Text(text)
SelectableView(text: text)
.layoutPriority(-1)
}
}
}
struct TextTestView: View {
@State private var text = "text"
var body: some View {
VStack {
SelectableContainer(text: text) {
VStack(alignment: .leading) {
Text("Header")
.font(.body)
Text(text)
.background(Color.orange)
}
}
.background(Color.yellow)
SelectableText(text)
.background(Color.black)
.foregroundColor(.white)
.font(.largeTitle)
}.padding()
}
}
let viewController = UIHostingController(rootView: TextTestView())
viewController.view.frame = CGRect(x: 0, y: 0, width: 400, height: 200)
PlaygroundPage.current.liveView = viewController.view
导入PlaygroundSupport
导入快捷键
私有类SelectableUIView:UIView{
变量文本:字符串?
重写初始化(帧:CGRect){
super.init(frame:frame)
self.setup()
}
必需的初始化?(编码器aDecoder:NSCoder){
super.init(编码者:aDecoder)
self.setup()
}
函数设置(){
self.addgestureerecognizer(ui长按gestureegnizer(目标:self,操作:#选择器(self.showMenu)))
}
@objc func显示菜单(识别器:UILongPressGestureRecognitor){
成为第一响应者()
let menu=UIMenuController.shared
如果!menu.ismenu可见{
menu.showMenu(from:self,rect:frame)
}
}
覆盖函数副本(uu发送方:有吗?){
let board=UIPasteboard.general
board.string=文本
UIMenuController.shared.hideMenu()
}
覆盖变量可以成为第一响应者:Bool{
返回真值
}
覆盖功能执行(uu操作:选择器,带sender sender:Any?->Bool{
返回操作==#选择器(UIResponderStandardEditActions.copy)
}
}
结构SelectableView:UIViewRepresentable{
变量文本:字符串
func makeUIView(上下文:context)->UIView{
let view=SelectableUIView()
返回视图
}
func updateUIView(uiView:uiView,context:context){
guard let view=uiView as?SelectableUIView-else{
返回
}
view.text=文本
}
}
结构SelectableContainer:视图{
私有出租内容:()->内容
私有变量文本:字符串
公共初始化(文本:字符串,@ViewBuilder内容:@escaping()->content){
self.text=文本
self.content=内容
}
公共机构:一些看法{
ZStack{
内容()
SelectableView(文本:文本)
.布局优先级(-1)
}
}
}
结构SelectableText:视图{
私有变量文本:字符串
公共初始化(uu文本:字符串){
self.text=文本
}
公共机构:一些看法{
ZStack{
文本(文本)
SelectableView(文本:文本)
.布局优先级(-1)
}
}
}
结构文本测试视图:视图{
@State private var text=“text”
var body:一些观点{
VStack{
SelectableContainer(文本:文本){
VStack(对齐:。前导){
文本(“标题”)
.font(.body)
文本(文本)
.背景(颜色.橙色)
}
}
.背景(颜色.黄色)
可选择文本(文本)
.背景(颜色.黑色)
.foregroundColor(.白色)
.font(.largeTitle)
}.padding()
}
}
让viewController=UIHostingController(rootView:TextTestView())
viewController.view.frame=CGRect(x:0,y:0,宽度:400,高度:200)
PlaygroundPage.current.liveView=viewController.view
操场视图
我发现一个简单的解决方案是只使用上下文菜单:
Text($someText)
.contextMenu(contextMenu)(菜单项:{
按钮(“复制”,操作:{
UIPasteboard.general.string=someText
})
}))
这方面有什么更新吗?我也会对此感兴趣。也许最好依赖UITextView而不是UITextField,并从其“isEditable”属性中获益。这解决了提到的两个问题。@Unknown对于迟来的评论表示抱歉。我添加的代码不显示键盘和自动更正。您可以使用textField.inputView=UIView()
和textF
SelectableText(text: text, isSelected: self.$isSelected)
.onTapGesture {
self.isSelected.toggle()
}
.onReceive(NotificationCenter.default.publisher(for: UIMenuController.willHideMenuNotification)) { _ in
self.isSelected = false
}
import PlaygroundSupport
import SwiftUI
private class SelectableUIView: UIView {
var text: String?
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
self.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)))
}
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.showMenu(from: self, rect: frame)
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = text
UIMenuController.shared.hideMenu()
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy)
}
}
struct SelectableView: UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> UIView {
let view = SelectableUIView()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
guard let view = uiView as? SelectableUIView else {
return
}
view.text = text
}
}
struct SelectableContainer<Content: View>: View {
private let content: () -> Content
private var text: String
public init(text: String, @ViewBuilder content: @escaping () -> Content) {
self.text = text
self.content = content
}
public var body: some View {
ZStack {
content()
SelectableView(text: text)
.layoutPriority(-1)
}
}
}
struct SelectableText: View {
private var text: String
public init(_ text: String) {
self.text = text
}
public var body: some View {
ZStack {
Text(text)
SelectableView(text: text)
.layoutPriority(-1)
}
}
}
struct TextTestView: View {
@State private var text = "text"
var body: some View {
VStack {
SelectableContainer(text: text) {
VStack(alignment: .leading) {
Text("Header")
.font(.body)
Text(text)
.background(Color.orange)
}
}
.background(Color.yellow)
SelectableText(text)
.background(Color.black)
.foregroundColor(.white)
.font(.largeTitle)
}.padding()
}
}
let viewController = UIHostingController(rootView: TextTestView())
viewController.view.frame = CGRect(x: 0, y: 0, width: 400, height: 200)
PlaygroundPage.current.liveView = viewController.view