MFMessageComposeViewController 禁用编辑文本条目?

MFMessageComposeViewController disable editing text entry?

你好 Whosebug 的朋友,

我正在集成一个 MFMessageViewController,我想通过禁止键盘出现或禁止用户与之交互来禁用它的编辑区域。目前我的代码是:

import UIKit
import MessageUI

class ViewController: UIViewController,MFMessageComposeViewControllerDelegate,UITextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }



  @IBAction func sendSmsClick(_ sender: AnyObject) {
            guard MFMessageComposeViewController.canSendText() else {
                return
            }

            let messageVC = MFMessageComposeViewController()
            UIButton.appearance(whenContainedInInstancesOf: [MFMessageComposeViewController.self]).isUserInteractionEnabled = false
            messageVC.body = "Enter a message hjhjhjkhjkhjhjhjjhgjhghjgjhghjghjghjghjgjhghjghjgjhghjghjghghjghjghjghghjghjhjghjghjghhvvvbnvhvhghghguyguygyugugigiugiouiopuopuoppuuo";
            messageVC.recipients = ["Enter tel-nr"]
            messageVC.messageComposeDelegate = self;
            NSLog("Subviews %@", messageVC.view.subviews);
           // self.view.endEditing(true)
        
        self.present(messageVC, animated: false) {
           // self.getAllSubviews(view: messageVC.view)
            messageVC.view.loopViewHierarchy { (view, stop) in
                if view is UIButton {
                    /// use the view
                    print("here")
                    stop = true
                }
            }
           
        }
        
        
        
        }

        func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
            switch (result.rawValue) {
                case MessageComposeResult.cancelled.rawValue:
                print("Message was cancelled")
                self.dismiss(animated: true, completion: nil)
            case MessageComposeResult.failed.rawValue:
                print("Message failed")
                self.dismiss(animated: true, completion: nil)
            case MessageComposeResult.sent.rawValue:
                print("Message was sent")
                self.dismiss(animated: true, completion: nil)
            default:
                break;
            }
        }

它工作正常,我只想了解键盘上方的特定 UIElement,我想禁用它以进行进一步编辑。我怎样才能做到这一点?

您可以尝试打印视图层次结构并从子视图中找到您的文本字段,问题是 - 所有这些 UI 都 运行 到与您的应用程序不同的环境中,您不会在那里找不到任何有用的东西。

/// Helper for printing view hierarchy recursively
extension UIView {
    func printViewHierarchy() {
        print(self)
        for view in self.subviews {
            view.printViewHierarchy()
        }
    }
}

self.present(messageVC, animated: true, completion: {
    // Try printing the view hierarchy after it has been loaded on to screen
    messageVC.view.printViewHierarchy()
})

// Here's what's printed by above
<UILayoutContainerView: 0x100a04990; frame = (0 0; 375 627); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x28100ef10>; layer = <CALayer: 0x281eac720>>
<UINavigationTransitionView: 0x100c0e0d0; frame = (0 0; 375 627); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x281eee860>>
<UIViewControllerWrapperView: 0x100c0f990; frame = (0 0; 375 627); autoresize = W+H; layer = <CALayer: 0x281e975e0>>
<UIView: 0x103b04900; frame = (0 0; 375 627); autoresize = W+H; layer = <CALayer: 0x281e8aa80>>
<_UISizeTrackingView: 0x103a05f80; frame = (0 0; 375 627); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x281e84c60>>
<_UIRemoteView: 0x103a077f0; frame = (0 0; 375 667); userInteractionEnabled = NO; layer = <CALayerHost: 0x281e84b20>>

_UIRemoteView 会妨碍您,您将无法在视图层次结构中找到您的目标 textField/button


我们还能做什么?

Use private apis that will most likely get us a rejection from App Store.


如何找到我们可以使用哪些私有 api?

ObjectiveC.runtime provides you ways to inspect class details (public + private).

import ObjectiveC.runtime

func printClassDetails(_ targetClass: AnyClass) {
    var varCount: UInt32 = 0
    let iVars = class_copyIvarList(targetClass, &varCount)

    var index = 0
    if let iVars = iVars {
        while index < varCount-1 {
            let iVar = iVars[index]
            if let name = ivar_getName(iVar) {
                print("iVar ------------> \(String(cString: name))")
            }
            index += 1
        }
    }
    free(iVars)

    index = 0
    let methods = class_copyMethodList(targetClass, &varCount)
    if let methods = methods {
        while index < varCount-1 {
            let method = methods[index]
            let selector = method_getName(method)
            print("method ------------> \(NSStringFromSelector(selector))")
            index += 1
        }
    }
    free(methods)
}

使用上面的代码,如果您尝试检查 MFMessageComposeViewController class,您将看到以下内容。

printClassDetails(MFMessageComposeViewController.self)
iVar ------------> _internal
iVar ------------> _messageComposeDelegate
iVar ------------> _recipients
iVar ------------> _body
iVar ------------> _subject
iVar ------------> _message
iVar ------------> _currentAttachedVideoCount
iVar ------------> _currentAttachedAudioCount
iVar ------------> _currentAttachedImageCount
iVar ------------> _UTITypes
iVar ------------> _photoIDs
iVar ------------> _cloudPhotoIDs
iVar ------------> _contentText
iVar ------------> _contentURLs
iVar ------------> _chatGUID
iVar ------------> _groupName
iVar ------------> _shareSheetSessionID
method ------------> recipients
method ------------> subject
method ------------> setGroupName:
method ------------> setRecipients:
method ------------> smsComposeControllerShouldSendMessageWithText:toRecipients:completion:
method ------------> attachments
method ------------> setMessageComposeDelegate:
method ------------> addRichLinkData:withWebpageURL:
method ------------> addAttachmentURL:withAlternateFilename:
method ------------> addAttachmentData:typeIdentifier:filename:
method ------------> setShareSheetSessionID:
method ------------> automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers
method ------------> message
method ------------> body
method ------------> setChatGUID:
method ------------> setMessage:
method ------------> chatGUID
method ------------> viewWillDisappear:
method ------------> contentText
method ------------> setSubject:
method ------------> viewDidLoad
method ------------> attachmentURLs
method ------------> groupName
method ------------> setUTITypes:
method ------------> dealloc
method ------------> viewDidAppear:
method ------------> viewWillAppear:
method ------------> UTITypes
method ------------> setContentText:
method ------------> setBody:
method ------------> smsComposeControllerCancelled:
method ------------> smsComposeControllerSendStarted:
method ------------> smsComposeControllerEntryViewContentInserted:
method ------------> .cxx_destruct
method ------------> photoIDs
method ------------> setModalPresentationStyle:
method ------------> setPhotoIDs:
method ------------> setContentURLs:
method ------------> setCloudPhotoIDs:
method ------------> initWithNibName:bundle:
method ------------> cloudPhotoIDs
method ------------> contentURLs
method ------------> shareSheetSessionID
method ------------> disableUserAttachments
method ------------> setCurrentAttachedVideoCount:
method ------------> setCurrentAttachedAudioCount:
method ------------> setCurrentAttachedImageCount:
method ------------> _MIMETypeForURL:
method ------------> _isVideoMIMEType:
method ------------> _isAudioMIMEType:
method ------------> _isImageMIMEType:
method ------------> _contentTypeForMIMEType:
method ------------> _updateAttachmentCountForAttachmentURL:
method ------------> canAddAttachmentURL:
method ------------> mutableAttachmentURLs
method ------------> addAttachmentData:withAlternateFilename:
method ------------> insertSharedItemAndReturnEntryViewFrame:withAlternateFilename:completion:
method ------------> showSharedItemInEntryView
method ------------> _setCanEditRecipients:
method ------------> _setShouldDisableEntryField:
method ------------> messageComposeDelegate
method ------------> currentAttachedVideoCount
method ------------> currentAttachedAudioCount

_setShouldDisableEntryField 看起来很有趣。如何使用?

let messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message"
messageVC.recipients = ["Test Telephone #"]

let name = [":","d","l","e","i","F","y","r","t","n","E","e","l","b","a","s","i","D","d","l","u","o","h","S","t","e","s","_"].reversed().joined()
let sel = NSSelectorFromString(name)
if messageVC.responds(to: sel) {
    messageVC.perform(sel, with: true)
}

self.present(messageVC, animated: true, completion: nil)

有效吗?

As of Xcode 12.5 & iOS 14.6 - it does.


为什么要费那么大劲才能找到选择器名称?

也许(这是一个很大的可能)这将有助于避免像下面这样的拒绝。

We identified one or more issues with a recent delivery for your app. Please correct the following issues, then upload again.

ITMS-90338: Non-public API usage - The app contains or inherits from non-public classes in Project: XXXXXXXXXXXXXX . If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions. In addition, note that one or more of the above APIs may be located in a static library that was included with your app. If so, they must be removed. For further information, visit the Technical Support Information at http://developer.apple.com/support/technical/