Migrate KMTextFieldDelegate to Swift

This commit is contained in:
Gabriel Wong 2017-10-06 12:47:28 +07:00
parent 6de84cc35e
commit 5fe6464177
7 changed files with 88 additions and 162 deletions

View file

@ -41,6 +41,7 @@
98F9D6AE195412A60087AA43 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F26071FD158F8C4600F37184 /* CFNetwork.framework */; };
98FFB3D41B67006F00289F0E /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FFB3D31B67006F00289F0E /* WebKit.framework */; };
98FFB3D51B67008000289F0E /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FFB3D31B67006F00289F0E /* WebKit.framework */; };
C0324B8D1F87480700AF3785 /* KeymanTextFieldDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8C1F87480700AF3785 /* KeymanTextFieldDelegateProxy.swift */; };
C040E50E1F85FF8A00901EE4 /* KeyPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C040E50D1F85FF8A00901EE4 /* KeyPreviewView.swift */; };
C040E5101F8606E300901EE4 /* KeymanTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C040E50F1F8606E300901EE4 /* KeymanTextField.swift */; };
C040E5121F86107E00901EE4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C040E5111F86107E00901EE4 /* AppDelegate.swift */; };
@ -66,12 +67,10 @@
C06D37441F81F5C400F61AE0 /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; };
C06D37451F81F5C400F61AE0 /* KMManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F273AB9815641EC500A47CEE /* KMManager.m */; };
C06D37461F81F5C400F61AE0 /* PopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A5FF361F6682EB00BE740C /* PopoverView.swift */; };
C06D37481F81F5C400F61AE0 /* KMTextFieldDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F273ABA21564419700A47CEE /* KMTextFieldDelegateProxy.m */; };
C06D37491F81F5C400F61AE0 /* KMTextViewDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CBD0DE1151EC30300D70779 /* KMTextViewDelegateProxy.m */; };
C06D374A1F81F5C400F61AE0 /* KMTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C0A1406151EA8B3007FA4AD /* KMTextView.m */; };
C06D374B1F81F5F400F61AE0 /* Keyman.h in Headers */ = {isa = PBXBuildFile; fileRef = F27FCB67157FFE0B00FBBA20 /* Keyman.h */; settings = {ATTRIBUTES = (Public, ); }; };
C06D374D1F81FFF900F61AE0 /* KMManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F273AB9715641EC500A47CEE /* KMManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C06D374F1F81FFF900F61AE0 /* KMTextFieldDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = F273ABA11564419700A47CEE /* KMTextFieldDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
C06D37521F82001100F61AE0 /* KMTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C0A1405151EA8B3007FA4AD /* KMTextView.h */; settings = {ATTRIBUTES = (Public, ); }; };
C06D37531F82001100F61AE0 /* KMTextViewDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CBD0DE0151EC30300D70779 /* KMTextViewDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
C06D37541F82002700F61AE0 /* CWLSynthesizeSingleton.h in Headers */ = {isa = PBXBuildFile; fileRef = F273AB9A15641F0E00A47CEE /* CWLSynthesizeSingleton.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -185,6 +184,7 @@
98F9D69A1954112F0087AA43 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
98F9D6CA19541D220087AA43 /* libKeyman.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libKeyman.a; path = build/libKeyman/libKeyman.a; sourceTree = "<group>"; };
98FFB3D31B67006F00289F0E /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
C0324B8C1F87480700AF3785 /* KeymanTextFieldDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanTextFieldDelegateProxy.swift; sourceTree = "<group>"; };
C034BBBD1F7CCE7D00021FF5 /* KeyboardMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardMenuView.swift; sourceTree = "<group>"; };
C040E50D1F85FF8A00901EE4 /* KeyPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPreviewView.swift; sourceTree = "<group>"; };
C040E50F1F8606E300901EE4 /* KeymanTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanTextField.swift; sourceTree = "<group>"; };
@ -221,8 +221,6 @@
F273AB9815641EC500A47CEE /* KMManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KMManager.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
F273AB9A15641F0E00A47CEE /* CWLSynthesizeSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CWLSynthesizeSingleton.h; sourceTree = "<group>"; };
F273AB9B1564207600A47CEE /* KMManager+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "KMManager+Internal.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
F273ABA11564419700A47CEE /* KMTextFieldDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KMTextFieldDelegateProxy.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
F273ABA21564419700A47CEE /* KMTextFieldDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KMTextFieldDelegateProxy.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
F27FCA7F157FB0DB00FBBA20 /* KME-iphoneos-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KME-iphoneos-Prefix.pch"; sourceTree = "<group>"; };
F27FCAC2157FD10A00FBBA20 /* KME-iphonesimulator-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KME-iphonesimulator-Prefix.pch"; sourceTree = "<group>"; };
F27FCAEE157FD59100FBBA20 /* Keyman-iphoneos.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Keyman-iphoneos.xcconfig"; sourceTree = "<group>"; };
@ -560,9 +558,8 @@
F273AB9E156440CD00A47CEE /* UITextField */ = {
isa = PBXGroup;
children = (
F273ABA11564419700A47CEE /* KMTextFieldDelegateProxy.h */,
F273ABA21564419700A47CEE /* KMTextFieldDelegateProxy.m */,
C040E50F1F8606E300901EE4 /* KeymanTextField.swift */,
C0324B8C1F87480700AF3785 /* KeymanTextFieldDelegateProxy.swift */,
);
name = UITextField;
sourceTree = "<group>";
@ -630,7 +627,6 @@
C06D372F1F81F4E100F61AE0 /* KeymanEngine.h in Headers */,
C06D374B1F81F5F400F61AE0 /* Keyman.h in Headers */,
C06D374D1F81FFF900F61AE0 /* KMManager.h in Headers */,
C06D374F1F81FFF900F61AE0 /* KMTextFieldDelegateProxy.h in Headers */,
C06D37521F82001100F61AE0 /* KMTextView.h in Headers */,
C06D37531F82001100F61AE0 /* KMTextViewDelegateProxy.h in Headers */,
C06D37541F82002700F61AE0 /* CWLSynthesizeSingleton.h in Headers */,
@ -863,10 +859,10 @@
C06D37431F81F5C400F61AE0 /* KeyboardPickerBarButtonItem.swift in Sources */,
C06D37441F81F5C400F61AE0 /* KeyboardNameTableViewCell.swift in Sources */,
C040E5101F8606E300901EE4 /* KeymanTextField.swift in Sources */,
C0324B8D1F87480700AF3785 /* KeymanTextFieldDelegateProxy.swift in Sources */,
C06D37451F81F5C400F61AE0 /* KMManager.m in Sources */,
C045148A1F85DF9100D88416 /* KeymanInputViewController.swift in Sources */,
C06D37461F81F5C400F61AE0 /* PopoverView.swift in Sources */,
C06D37481F81F5C400F61AE0 /* KMTextFieldDelegateProxy.m in Sources */,
C06D37491F81F5C400F61AE0 /* KMTextViewDelegateProxy.m in Sources */,
C06D374A1F81F5C400F61AE0 /* KMTextView.m in Sources */,
);

View file

@ -1,30 +0,0 @@
//
// KMTextFieldDelegateProxy.h
// Keyman Engine
//
// Copyright (c) 2017 SIL International. All rights reserved.
//
//
// Proxies delegate messages for a KMTextField.
// This allows the KMTextField to hook into these calls while allowing a developer to still
// use the delegate as normal (albeit with a different name: 'keymanDelegate')
//
// This class is required because at the time of writing, setting a UITextField as it's
// own delegate caused an infinite loop in Apple's code, repeatedly calling the selectionDidChange callback
// See: http://lists.apple.com/archives/cocoa-dev/2010/Feb/msg01391.html
#import <UIKit/UIKit.h>
@protocol KMTextFieldDelegate;
@interface KMTextFieldDelegateProxy : NSObject <UITextFieldDelegate> {
id<KMTextFieldDelegate> __weak keymanDelegate_;
}
@property (nonatomic, weak) id<KMTextFieldDelegate> keymanDelegate;
@end
@protocol KMTextFieldDelegate <UITextFieldDelegate>
// Setting this protocol space aside in case we later want to add additional delegate methods for KMTextField
@end

View file

@ -1,101 +0,0 @@
//
// KMTextFieldDelegateProxy.m
// Keyman Engine
//
// Created by Patrick Weekes on 12-05-16.
// Copyright (c) 2017 SIL International. All rights reserved.
//
#import "KMTextFieldDelegateProxy.h"
#import "KeymanEngine/KeymanEngine-Swift.h"
@implementation KMTextFieldDelegateProxy
@synthesize keymanDelegate = keymanDelegate_;
#pragma mark - Object Admin
- (void)dealloc {
self.keymanDelegate = nil;
}
#pragma mark - UITextFieldDelegate
// NOTE: the return value from the textField's hook is ignored
- (BOOL)textFieldShouldBeginEditing:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
[textField textFieldShouldBeginEditing:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
return [self.keymanDelegate textFieldShouldBeginEditing:textField];
}
return YES;
}
- (void)textFieldDidBeginEditing:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
[textField textFieldDidBeginEditing:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
[self.keymanDelegate textFieldDidBeginEditing:textField];
}
}
// NOTE: the return value from the textField's hook is ignored
- (BOOL)textFieldShouldEndEditing:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
[textField textFieldShouldEndEditing:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
return [self.keymanDelegate textFieldShouldEndEditing:textField];
}
return YES;
}
- (void)textFieldDidEndEditing:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldDidEndEditing:)]) {
[textField textFieldDidEndEditing:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) {
[self.keymanDelegate textFieldDidEndEditing:textField];
}
}
// NOTE: the return value from the textField's hook is ignored
- (BOOL)textField:(KeymanTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([textField respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
[textField textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
if ([self.keymanDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.keymanDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
return YES;
}
// NOTE: the return value from the textField's hook is ignored
- (BOOL)textFieldShouldClear:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldShouldClear:)]) {
[textField textFieldShouldClear:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldShouldClear:)]) {
return [self.keymanDelegate textFieldShouldClear:textField];
}
return YES;
}
// NOTE: the return value from the textField's hook is ignored
- (BOOL)textFieldShouldReturn:(KeymanTextField *)textField {
if ([textField respondsToSelector:@selector(textFieldShouldReturn:)]) {
[textField textFieldShouldReturn:textField];
}
if ([self.keymanDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
return [self.keymanDelegate textFieldShouldReturn:textField];
}
return YES;
}
@end

View file

@ -17,7 +17,7 @@ public class KeymanTextField: UITextField, UITextFieldDelegate {
public var shouldSetCustomFontOnKeyboardChange = true
public var isInputClickSoundEnabled = true
private var delegateProxy: KMTextFieldDelegateProxy?
private var delegateProxy: KeymanTextFieldDelegateProxy!
private var shouldUpdateKMText = false
// MARK: - Object Admin
@ -32,27 +32,16 @@ public class KeymanTextField: UITextField, UITextFieldDelegate {
public override init(frame: CGRect) {
super.init(frame: frame)
delegateProxy = KMTextFieldDelegateProxy()
delegate = delegateProxy
if #available(iOS 9.0, *) {
inputAssistantItem.leadingBarButtonGroups = []
inputAssistantItem.trailingBarButtonGroups = []
}
KMManager.sharedInstance() // Preload webview keyboard
NotificationCenter.default.addObserver(self, selector: #selector(self.textFieldTextDidChange),
name: .UITextFieldTextDidChange, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged),
name: NSNotification.Name.keymanKeyboardChanged, object: nil)
performCommonInit()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
performCommonInit()
}
delegateProxy = KMTextFieldDelegateProxy(self)
private func performCommonInit() {
delegateProxy = KeymanTextFieldDelegateProxy(self)
delegate = delegateProxy
if #available(iOS 9.0, *) {
@ -66,6 +55,7 @@ public class KeymanTextField: UITextField, UITextFieldDelegate {
name: .UITextFieldTextDidChange, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged),
name: NSNotification.Name.keymanKeyboardChanged, object: nil)
}
// MARK: - Class Overrides
@ -104,10 +94,10 @@ public class KeymanTextField: UITextField, UITextFieldDelegate {
// MARK: - Public Methods
// Use this KMTextFieldDelegate instead of the normal UITextFieldDelegate.
// Use this KeymanTextFieldDelegate instead of the normal UITextFieldDelegate.
// All of the normal UITextFieldDelegate methods are supported.
@objc public func setKeymanDelegate(_ keymanDelegate: KMTextFieldDelegate?) {
delegateProxy?.keymanDelegate = keymanDelegate
public func setKeymanDelegate(_ keymanDelegate: KeymanTextFieldDelegate?) {
delegateProxy.keymanDelegate = keymanDelegate
KMManager.sharedInstance().kmLog(
"KeymanTextField: \(self.debugDescription) keymanDelegate set to: \(keymanDelegate.debugDescription)",
checkDebugPrinting: true)

View file

@ -0,0 +1,73 @@
//
// KeymanTextFieldDelegateProxy.swift
// KeymanEngine
//
// Created by Gabriel Wong on 2017-10-06.
// Copyright © 2017 SIL International. All rights reserved.
//
// Proxies delegate messages for a KeymanTextField.
// This allows the KeymanTextField to hook into these calls while allowing a developer to still
// use the delegate as normal (albeit with a different name: 'keymanDelegate')
//
// This class is required because at the time of writing, setting a UITextField as it's
// own delegate caused an infinite loop in Apple's code, repeatedly calling the selectionDidChange callback
// See: http://lists.apple.com/archives/cocoa-dev/2010/Feb/msg01391.html
import UIKit
public protocol KeymanTextFieldDelegate: UITextFieldDelegate { }
class KeymanTextFieldDelegateProxy: NSObject, UITextFieldDelegate {
weak var keymanDelegate: KeymanTextFieldDelegate?
private unowned let textField: UITextFieldDelegate
init(_ textField: KeymanTextField) {
self.textField = textField
super.init()
}
// MARK: - UITextFieldDelegate
// NOTE: Return values from the KeymanTextField hooks are ignored
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
_ = self.textField.textFieldShouldBeginEditing?(textField)
return keymanDelegate?.textFieldShouldBeginEditing?(textField) ?? true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.textField.textFieldDidBeginEditing?(textField)
keymanDelegate?.textFieldDidBeginEditing?(textField)
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
_ = self.textField.textFieldShouldEndEditing?(textField)
return keymanDelegate?.textFieldShouldEndEditing?(textField) ?? true
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.textField.textFieldDidEndEditing?(textField)
keymanDelegate?.textFieldDidEndEditing?(textField)
}
@available(iOS 10.0, *)
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.textField.textFieldDidEndEditing?(textField, reason: reason)
keymanDelegate?.textFieldDidEndEditing?(textField, reason: reason)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
_ = self.textField.textField?(textField, shouldChangeCharactersIn: range, replacementString: string)
return keymanDelegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) ?? true
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
_ = self.textField.textFieldShouldClear?(textField)
return keymanDelegate?.textFieldShouldClear?(textField) ?? true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
_ = self.textField.textFieldShouldReturn?(textField)
return keymanDelegate?.textFieldShouldReturn?(textField) ?? true
}
}

View file

@ -11,5 +11,3 @@
#import "KMTextView.h"
#import "KMTextViewDelegateProxy.h"
#import "KMTextFieldDelegateProxy.h"

View file

@ -10,7 +10,7 @@ import KeymanEngine
import UIKit
import QuartzCore
class MainViewController: UIViewController, UIAlertViewDelegate, KMTextViewDelegate, KMTextFieldDelegate {
class MainViewController: UIViewController, UIAlertViewDelegate, KMTextViewDelegate, KeymanTextFieldDelegate {
var textView1: KMTextView!
var textView2: KeymanTextField!
var textView3: UITextView!