Compare commits

...

8 Commits

Author SHA1 Message Date
Darren Ford 637703bd5e Update documentation 2023-03-15 12:25:08 +11:00
Darren Ford ad85f71bd2 Moved 'isSecure' out into two different classes
In later versions of Xcode changing the NSTextFieldCell to an NSSecureTextFieldCell generates an error (no longer secure).

Rather than maintain two almost identical field types, GYB is being used to templatize the primary source file 'DSFFloatLabelledTextField.swift.gyb' into two different classes.

As a result, changes _must_ be made to the template file rather than the sources.
2023-03-15 12:16:35 +11:00
Darren Ford 0e89b0c162 Fixed podspec 2023-03-15 11:20:41 +11:00
Darren Ford 344eab8512 First cut refactor secure 2023-03-15 11:06:11 +11:00
Darren Ford 35523f5ec7 Added secure text field support, delegate support, updated doco 2020-12-01 14:14:46 +11:00
Darren Ford 8981182b38 Better accent color handling for big sur and legacy OSes. 2020-12-01 13:59:57 +11:00
Darren Ford 7e5864260b Use Google Translate to make Arabic RTL demo 2020-12-01 12:49:08 +11:00
Darren Ford c47631f3e7 Added delegate callbacks 2020-11-30 10:27:00 +11:00
37 changed files with 3094 additions and 485 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
DSFFloatLabelledTextControls.xcodeproj/xcuserdata
xcschememanagement.plist
.swiftpm
Demos/DSFFloatLabelledTextControls.xcodeproj/xcuserdata
gyb.cpython-310.pyc

View File

@ -1,9 +1,9 @@
Pod::Spec.new do |s|
s.name = "DSFFloatLabelledTextControl"
s.version = "1.7.0"
s.summary = "A macOS Cocoa single-line NSTextField that implements the Float Label Pattern"
s.version = "2.0.0"
s.summary = "A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern"
s.description = <<-DESC
A macOS Cocoa single-line NSTextField that implements the Float Label Pattern.
A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern.
DESC
s.homepage = "https://github.com/dagronf/DSFFloatLabelledTextControl"
s.license = { :type => "MIT", :file => "LICENSE" }
@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.social_media_url = ""
s.osx.deployment_target = "10.11"
s.source = { :git => ".git", :tag => s.version.to_s }
s.source_files = "DSFFloatLabelledTextControl/DSFFloatLabelledTextField.swift"
s.source_files = "Sources/DSFFloatLabelledTextField/**/*.{swift}"
s.frameworks = "Cocoa"
s.swift_version = "5.0"
s.swift_version = "5.4"
end

View File

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>DSFFloatLabelledTextControl.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>DSFFloatLabelledTextControls Demo objc.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>DSFFloatLabelledTextControls RTL.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>DSFFloatLabelledTextControls.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>DSFFloatLabelledTextField.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>2351F84A2207D15C00EFA4F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>23A2B7E8220792FE00B2FC77</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>23C3AB9923FBCFA30079D9C3</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -3,41 +3,50 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXAggregateTarget section */
23E825612575D1760023A3C7 /* All */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 23E825642575D1760023A3C7 /* Build configuration list for PBXAggregateTarget "All" */;
buildPhases = (
);
dependencies = (
23E825682575D17E0023A3C7 /* PBXTargetDependency */,
23E8256A2575D17E0023A3C7 /* PBXTargetDependency */,
);
name = All;
productName = All;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
2351F84F2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */; settings = {ATTRIBUTES = (Public, ); }; };
2351F8522207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; };
2351F8532207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2351F8582207D16100EFA4F7 /* DSFFloatLabelledTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */; };
2351F85B22082AD800EFA4F7 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 2351F85922082AD800EFA4F7 /* LICENSE */; };
2351F85C22082AD800EFA4F7 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 2351F85A22082AD800EFA4F7 /* README.md */; };
23A2B7ED220792FE00B2FC77 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2B7EC220792FE00B2FC77 /* AppDelegate.swift */; };
23A2B7EF220792FF00B2FC77 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23A2B7EE220792FF00B2FC77 /* Assets.xcassets */; };
23A2B7F2220792FF00B2FC77 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 23A2B7F0220792FF00B2FC77 /* MainMenu.xib */; };
23A3061C29C12CB200BEC98A /* DSFFloatLabelledTextField in Frameworks */ = {isa = PBXBuildFile; productRef = 23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */; };
23A3061E29C12CB800BEC98A /* DSFFloatLabelledTextField in Frameworks */ = {isa = PBXBuildFile; productRef = 23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */; };
23C3AB9E23FBCFA30079D9C3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 23C3AB9D23FBCFA30079D9C3 /* AppDelegate.m */; };
23C3ABA023FBCFA40079D9C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23C3AB9F23FBCFA40079D9C3 /* Assets.xcassets */; };
23C3ABA323FBCFA40079D9C3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 23C3ABA123FBCFA40079D9C3 /* MainMenu.xib */; };
23C3ABA623FBCFA40079D9C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 23C3ABA523FBCFA40079D9C3 /* main.m */; };
23C3ABAC23FBCFDE0079D9C3 /* DSFFloatLabelledTextField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; };
23C3ABB023FBD0F80079D9C3 /* DSFFloatLabelledTextField.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
2351F8502207D15C00EFA4F7 /* PBXContainerItemProxy */ = {
23E825672575D17E0023A3C7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 23A2B7E1220792FE00B2FC77 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 2351F84A2207D15C00EFA4F7;
remoteInfo = DSFFloatLabelledTextControl;
remoteGlobalIDString = 23A2B7E8220792FE00B2FC77;
remoteInfo = DSFFloatLabelledTextControls;
};
23C3ABAD23FBCFE40079D9C3 /* PBXContainerItemProxy */ = {
23E825692575D17E0023A3C7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 23A2B7E1220792FE00B2FC77 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 2351F84A2207D15C00EFA4F7;
remoteInfo = DSFFloatLabelledTextField;
remoteGlobalIDString = 23C3AB9923FBCFA30079D9C3;
remoteInfo = "DSFFloatLabelledTextControls Demo objc";
};
/* End PBXContainerItemProxy section */
@ -48,7 +57,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
2351F8532207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -59,26 +67,19 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
23C3ABB023FBD0F80079D9C3 /* DSFFloatLabelledTextField.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DSFFloatLabelledTextField.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSFFloatLabelledTextControl.h; sourceTree = "<group>"; };
2351F84E2207D15C00EFA4F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2351F85922082AD800EFA4F7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
2351F85A22082AD800EFA4F7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DSFFloatLabelledTextControls.app; sourceTree = BUILT_PRODUCTS_DIR; };
23A2B7EC220792FE00B2FC77 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
23A2B7EE220792FF00B2FC77 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
23A2B7F1220792FF00B2FC77 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
23A2B7F3220792FF00B2FC77 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
23A2B7F4220792FF00B2FC77 /* DSFFloatLabelledTextControls.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls.entitlements; sourceTree = "<group>"; };
23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSFFloatLabelledTextField.swift; sourceTree = "<group>"; };
23ACF96D228A3F6600FF6594 /* DSFFloatLabelledTextControl.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = DSFFloatLabelledTextControl.podspec; sourceTree = "<group>"; };
23A3061A29C12C9C00BEC98A /* DSFFloatLabelledTextControls */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DSFFloatLabelledTextControls; path = ..; sourceTree = "<group>"; };
23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFFloatLabelledTextControls Demo objc.app"; sourceTree = BUILT_PRODUCTS_DIR; };
23C3AB9C23FBCFA30079D9C3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
23C3AB9D23FBCFA30079D9C3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@ -87,22 +88,15 @@
23C3ABA423FBCFA40079D9C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
23C3ABA523FBCFA40079D9C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
23C3ABA723FBCFA40079D9C3 /* DSFFloatLabelledTextControls_Demo_objc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls_Demo_objc.entitlements; sourceTree = "<group>"; };
23FFF1CD23BD490F008FA55A /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
23E8256D2575D28A0023A3C7 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/MainMenu.strings; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2351F8482207D15C00EFA4F7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
23A2B7E6220792FE00B2FC77 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2351F8522207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Frameworks */,
23A3061C29C12CB200BEC98A /* DSFFloatLabelledTextField in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -110,32 +104,18 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
23C3ABAC23FBCFDE0079D9C3 /* DSFFloatLabelledTextField.framework in Frameworks */,
23A3061E29C12CB800BEC98A /* DSFFloatLabelledTextField in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2351F84C2207D15C00EFA4F7 /* DSFFloatLabelledTextField */ = {
isa = PBXGroup;
children = (
23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */,
2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */,
2351F84E2207D15C00EFA4F7 /* Info.plist */,
);
path = DSFFloatLabelledTextField;
sourceTree = "<group>";
};
23A2B7E0220792FE00B2FC77 = {
isa = PBXGroup;
children = (
23A3061A29C12C9C00BEC98A /* DSFFloatLabelledTextControls */,
23FFF1CF23BD4982008FA55A /* Demos */,
23FFF1CE23BD496A008FA55A /* Sources */,
23ACF96D228A3F6600FF6594 /* DSFFloatLabelledTextControl.podspec */,
2351F85922082AD800EFA4F7 /* LICENSE */,
2351F85A22082AD800EFA4F7 /* README.md */,
23FFF1CD23BD490F008FA55A /* Package.swift */,
23A2B7EA220792FE00B2FC77 /* Products */,
23C3ABAB23FBCFDE0079D9C3 /* Frameworks */,
);
@ -145,7 +125,6 @@
isa = PBXGroup;
children = (
23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */,
2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */,
23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */,
);
name = Products;
@ -184,14 +163,6 @@
name = Frameworks;
sourceTree = "<group>";
};
23FFF1CE23BD496A008FA55A /* Sources */ = {
isa = PBXGroup;
children = (
2351F84C2207D15C00EFA4F7 /* DSFFloatLabelledTextField */,
);
path = Sources;
sourceTree = "<group>";
};
23FFF1CF23BD4982008FA55A /* Demos */ = {
isa = PBXGroup;
children = (
@ -203,36 +174,7 @@
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
2351F8462207D15C00EFA4F7 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
2351F84F2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2351F8542207D15C00EFA4F7 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextField" */;
buildPhases = (
2351F8462207D15C00EFA4F7 /* Headers */,
2351F8472207D15C00EFA4F7 /* Sources */,
2351F8482207D15C00EFA4F7 /* Frameworks */,
2351F8492207D15C00EFA4F7 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = DSFFloatLabelledTextField;
productName = DSFFloatLabelledTextControl;
productReference = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */;
productType = "com.apple.product-type.framework";
};
23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */ = {
isa = PBXNativeTarget;
buildConfigurationList = 23A2B7F7220792FF00B2FC77 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextControls" */;
@ -245,9 +187,11 @@
buildRules = (
);
dependencies = (
2351F8512207D15C00EFA4F7 /* PBXTargetDependency */,
);
name = DSFFloatLabelledTextControls;
packageProductDependencies = (
23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */,
);
productName = DSFFloatLabelledTextControls;
productReference = 23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */;
productType = "com.apple.product-type.application";
@ -264,9 +208,11 @@
buildRules = (
);
dependencies = (
23C3ABAE23FBCFE40079D9C3 /* PBXTargetDependency */,
);
name = "DSFFloatLabelledTextControls Demo objc";
packageProductDependencies = (
23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */,
);
productName = "DSFFloatLabelledTextControls Demo objc";
productReference = 23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */;
productType = "com.apple.product-type.application";
@ -281,15 +227,15 @@
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Darren Ford";
TargetAttributes = {
2351F84A2207D15C00EFA4F7 = {
CreatedOnToolsVersion = 10.1;
};
23A2B7E8220792FE00B2FC77 = {
CreatedOnToolsVersion = 10.1;
};
23C3AB9923FBCFA30079D9C3 = {
CreatedOnToolsVersion = 11.3.1;
};
23E825612575D1760023A3C7 = {
CreatedOnToolsVersion = 12.2;
};
};
};
buildConfigurationList = 23A2B7E4220792FE00B2FC77 /* Build configuration list for PBXProject "DSFFloatLabelledTextControls" */;
@ -299,34 +245,26 @@
knownRegions = (
en,
Base,
ar,
);
mainGroup = 23A2B7E0220792FE00B2FC77;
productRefGroup = 23A2B7EA220792FE00B2FC77 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */,
23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */,
23C3AB9923FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc */,
23E825612575D1760023A3C7 /* All */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
2351F8492207D15C00EFA4F7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
23A2B7E7220792FE00B2FC77 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2351F85B22082AD800EFA4F7 /* LICENSE in Resources */,
23A2B7EF220792FF00B2FC77 /* Assets.xcassets in Resources */,
2351F85C22082AD800EFA4F7 /* README.md in Resources */,
23A2B7F2220792FF00B2FC77 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -343,14 +281,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2351F8472207D15C00EFA4F7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2351F8582207D16100EFA4F7 /* DSFFloatLabelledTextField.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
23A2B7E5220792FE00B2FC77 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -371,15 +301,15 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
2351F8512207D15C00EFA4F7 /* PBXTargetDependency */ = {
23E825682575D17E0023A3C7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */;
targetProxy = 2351F8502207D15C00EFA4F7 /* PBXContainerItemProxy */;
target = 23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */;
targetProxy = 23E825672575D17E0023A3C7 /* PBXContainerItemProxy */;
};
23C3ABAE23FBCFE40079D9C3 /* PBXTargetDependency */ = {
23E8256A2575D17E0023A3C7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */;
targetProxy = 23C3ABAD23FBCFE40079D9C3 /* PBXContainerItemProxy */;
target = 23C3AB9923FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc */;
targetProxy = 23E825692575D17E0023A3C7 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@ -388,6 +318,7 @@
isa = PBXVariantGroup;
children = (
23A2B7F1220792FF00B2FC77 /* Base */,
23E8256D2575D28A0023A3C7 /* ar */,
);
name = MainMenu.xib;
sourceTree = "<group>";
@ -403,70 +334,11 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
2351F8552207D15C00EFA4F7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = Sources/DSFFloatLabelledTextField/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.5;
PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControl;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.2;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
2351F8562207D15C00EFA4F7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = Sources/DSFFloatLabelledTextField/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.5;
PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControl;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.2;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
23A2B7F5220792FF00B2FC77 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -528,6 +400,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -581,8 +454,8 @@
23A2B7F8220792FF00B2FC77 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
CODE_SIGN_ENTITLEMENTS = Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
@ -593,6 +466,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 1.5;
PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControls;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -604,8 +478,8 @@
23A2B7F9220792FF00B2FC77 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
CODE_SIGN_ENTITLEMENTS = Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
@ -616,6 +490,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 1.5;
PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControls;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -627,19 +502,20 @@
23C3ABA823FBCFA40079D9C3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 3L6RK3LGGW;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "Demos/DSFFloatLabelledTextControls Demo objc/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFFloatLabelledTextControls-Demo-objc";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@ -648,36 +524,46 @@
23C3ABA923FBCFA40079D9C3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 3L6RK3LGGW;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "Demos/DSFFloatLabelledTextControls Demo objc/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFFloatLabelledTextControls-Demo-objc";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
23E825622575D1760023A3C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 3L6RK3LGGW;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
23E825632575D1760023A3C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 3L6RK3LGGW;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2351F8542207D15C00EFA4F7 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextField" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2351F8552207D15C00EFA4F7 /* Debug */,
2351F8562207D15C00EFA4F7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
23A2B7E4220792FE00B2FC77 /* Build configuration list for PBXProject "DSFFloatLabelledTextControls" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -705,7 +591,27 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
23E825642575D1760023A3C7 /* Build configuration list for PBXAggregateTarget "All" */ = {
isa = XCConfigurationList;
buildConfigurations = (
23E825622575D1760023A3C7 /* Debug */,
23E825632575D1760023A3C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */ = {
isa = XCSwiftPackageProductDependency;
productName = DSFFloatLabelledTextField;
};
23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */ = {
isa = XCSwiftPackageProductDependency;
productName = DSFFloatLabelledTextField;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 23A2B7E1220792FE00B2FC77 /* Project object */;
}

View File

@ -34,13 +34,14 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = "IDELaunchSchemeLanguageRLO"
language = "ar"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
showNonLocalizedStrings = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference

View File

@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@ -38,8 +36,8 @@
ReferencedContainer = "container:DSFFloatLabelledTextControls.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -61,8 +59,6 @@
ReferencedContainer = "container:DSFFloatLabelledTextControls.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -8,7 +8,9 @@
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@import DSFFloatLabelledTextField;
@interface AppDelegate : NSObject <NSApplicationDelegate, DSFFloatLabelledTextFieldDelegate>
@end

View File

@ -24,5 +24,8 @@
// Insert code here to tear down your application
}
- (void)floatLabelledTextFieldContentChanged:(DSFFloatLabelledSecureTextField *)field {
NSLog(@"Password is now `%@`", field.stringValue);
}
@end

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -684,50 +684,77 @@
<window title="DSFFloatLabelledTextControls Demo objc" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="107"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="107"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="contentRect" x="335" y="390" width="480" height="145"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="138"/>
<subviews>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="XPK-cR-iSb" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="20" y="55" width="440" height="32"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Title" id="PFO-GA-4nl">
<font key="font" metaFont="system" size="24"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="16"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="257-gt-5Ui" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="20" y="20" width="440" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Subtitle text goes here" id="Msd-kf-k0Q">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="13"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="250" verticalStackHuggingPriority="249.99998474121094" ambiguous="YES" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5Xn-w2-ecV">
<rect key="frame" x="20" y="27" width="440" height="98"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="XPK-cR-iSb" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="66" width="440" height="32"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Title" id="PFO-GA-4nl">
<font key="font" metaFont="system" size="24"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="16"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="5uu-Hi-xn8" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="33" width="440" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Subtitle text goes here" id="Uic-HN-dxQ">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="13"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="257-gt-5Ui" userLabel="Title Field" customClass="DSFFloatLabelledSecureTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="0.0" width="440" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="My Secret Password" id="Msd-kf-k0Q">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="13"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="fDf-Y2-VaF"/>
</connections>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstItem="257-gt-5Ui" firstAttribute="trailing" secondItem="XPK-cR-iSb" secondAttribute="trailing" id="6wQ-8C-VXo"/>
<constraint firstItem="XPK-cR-iSb" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" id="Dfi-58-3cv"/>
<constraint firstItem="257-gt-5Ui" firstAttribute="leading" secondItem="XPK-cR-iSb" secondAttribute="leading" id="Pf1-5G-WVc"/>
<constraint firstItem="XPK-cR-iSb" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="mRz-gB-T7B"/>
<constraint firstAttribute="trailing" secondItem="XPK-cR-iSb" secondAttribute="trailing" constant="20" symbolic="YES" id="pRo-7r-rd2"/>
<constraint firstAttribute="bottom" secondItem="257-gt-5Ui" secondAttribute="bottom" constant="20" id="pct-fO-Xxt"/>
<constraint firstItem="257-gt-5Ui" firstAttribute="top" secondItem="XPK-cR-iSb" secondAttribute="bottom" constant="10" id="wMY-qA-eat"/>
<constraint firstAttribute="trailing" secondItem="5Xn-w2-ecV" secondAttribute="trailing" constant="20" symbolic="YES" id="FBf-0I-IIa"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="5Xn-w2-ecV" secondAttribute="bottom" constant="20" symbolic="YES" id="Sdh-oS-cCe"/>
<constraint firstItem="5Xn-w2-ecV" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" symbolic="YES" id="wLY-Yu-p9z"/>
<constraint firstItem="5Xn-w2-ecV" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" symbolic="YES" id="ztN-wM-TMN"/>
</constraints>
</view>
<point key="canvasLocation" x="139" y="-246.5"/>
<point key="canvasLocation" x="139" y="-227.5"/>
</window>
</objects>
</document>

View File

@ -35,6 +35,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var topFloatingLabel: DSFFloatLabelledTextField!
@IBOutlet weak var secureTextField: DSFFloatLabelledSecureTextField!
@IBOutlet weak var passwordFloatingLabel: DSFFloatLabelledTextField!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
@ -45,27 +48,36 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let cv = self.window.contentView!
let x = NSLayoutConstraint(item: ft, attribute: .width,
relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute,
multiplier: 1, constant: 300)
let x = NSLayoutConstraint(
item: ft, attribute: .width,
relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute,
multiplier: 1, constant: 300
)
ft.addConstraint(x)
cv.addSubview(ft)
let x1 = NSLayoutConstraint(item: ft, attribute: .left,
relatedBy: .equal,
toItem: cv, attribute: .left,
multiplier: 1, constant: 20)
let x1 = NSLayoutConstraint(
item: ft, attribute: .leading,
relatedBy: .equal,
toItem: cv, attribute: .leading,
multiplier: 1, constant: 20
)
cv.addConstraint(x1)
let x2 = NSLayoutConstraint(item: ft, attribute: .bottom,
relatedBy: .equal,
toItem: cv, attribute: .bottom,
multiplier: 1, constant: -20)
let x2 = NSLayoutConstraint(
item: ft, attribute: .bottom,
relatedBy: .equal,
toItem: cv, attribute: .bottom,
multiplier: 1, constant: -20
)
cv.addConstraint(x2)
self.secureTextField.stringValue = "caterpillar"
}
@IBAction func resetPressed(_ sender: Any) {
// Verify that when we programatically change the string value the control updates accordingly
self.topFloatingLabel.stringValue = "Reset the content"
@ -75,6 +87,34 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
extension AppDelegate: DSFFloatLabelledTextFieldDelegate {
func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didShowFloatingLabel didShow: Bool) {
if field === topFloatingLabel {
if didShow {
Swift.print("Title Field floating label is now visible")
}
else {
Swift.print("Title Field floating label is no longer visible")
}
}
}
func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didFocus: Bool) {
if field === topFloatingLabel {
if didFocus {
Swift.print("Title Field did focus")
}
else {
Swift.print("Title Field lost focus")
}
}
}
func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelledTextField) {
if field === passwordFloatingLabel {
Swift.print("Password is now '\(self.passwordFloatingLabel.stringValue)'")
}
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17154"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -15,6 +15,8 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="DSFFloatLabelledTextControls" customModuleProvider="target">
<connections>
<outlet property="passwordFloatingLabel" destination="VLP-0u-Kf3" id="9Lw-el-n78"/>
<outlet property="secureTextField" destination="VLP-0u-Kf3" id="uDf-HC-5AQ"/>
<outlet property="topFloatingLabel" destination="tnz-85-NdV" id="Pyt-zO-up9"/>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
@ -685,21 +687,21 @@
<window title="Floating Labels Test" allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="497" height="441"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="contentRect" x="335" y="390" width="497" height="592"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="497" height="461"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="592"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<box boxType="secondary" title="Purchase Form" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="pnz-8U-z2F">
<rect key="frame" x="17" y="303" width="463" height="140"/>
<view key="contentView" id="fUJ-lp-O9s">
<rect key="frame" x="3" y="3" width="457" height="134"/>
<rect key="frame" x="17" y="432" width="463" height="142"/>
<view key="contentView" ambiguous="YES" id="fUJ-lp-O9s">
<rect key="frame" x="4" y="5" width="455" height="134"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="tnz-85-NdV" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="12" y="97" width="366" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" title="This is a fusdfjk" placeholderString="Title" id="ETD-Tb-a7N">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tnz-85-NdV" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="12" y="97" width="374" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" title="Groovy Software v1" placeholderString="Title" id="ETD-Tb-a7N">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -709,9 +711,12 @@
<real key="value" value="13"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="62z-Xl-apm"/>
</connections>
</textField>
<button toolTip="Check for programattic changing of stringValue" horizontalHuggingPriority="999" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nTi-Ym-3BC">
<rect key="frame" x="380" y="92" width="75" height="32"/>
<button toolTip="Check for programattic changing of stringValue" horizontalHuggingPriority="999" verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nTi-Ym-3BC">
<rect key="frame" x="387" y="93" width="69" height="32"/>
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pTp-yr-DKx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -720,7 +725,7 @@
<action selector="resetPressed:" target="Voe-Tx-rLC" id="keN-c6-1wD"/>
</connections>
</button>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="pyP-dk-pAy">
<box verticalHuggingPriority="750" ambiguous="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="pyP-dk-pAy">
<rect key="frame" x="12" y="86" width="437" height="5"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="zbB-HI-AVu" userLabel="Price Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
@ -742,7 +747,7 @@
<box horizontalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="Mrp-MT-gGa">
<rect key="frame" x="117" y="46" width="5" height="43"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="SAo-aL-Ofe" userLabel="Location Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SAo-aL-Ofe" userLabel="Location Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="128" y="55" width="321" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" placeholderString="Specific Location (Optional)" id="BHQ-MA-tIJ">
<font key="font" metaFont="system" size="18"/>
@ -755,10 +760,10 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="YiQ-45-hxx">
<box verticalHuggingPriority="750" ambiguous="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="YiQ-45-hxx">
<rect key="frame" x="12" y="44" width="437" height="5"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" translatesAutoresizingMaskIntoConstraints="NO" id="h8z-Ku-xLD" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="h8z-Ku-xLD" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="12" y="13" width="437" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" placeholderString="Description" id="XtC-Dr-5RL">
<font key="font" metaFont="system" size="18"/>
@ -800,10 +805,10 @@
</view>
</box>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="CVr-p8-VOx">
<rect key="frame" x="20" y="273" width="457" height="5"/>
<rect key="frame" x="20" y="402" width="457" height="5"/>
</box>
<stackView distribution="fillEqually" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="waN-7O-qLn">
<rect key="frame" x="20" y="212" width="457" height="33"/>
<rect key="frame" x="20" y="341" width="457" height="33"/>
<subviews>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="oU9-jl-8Ow" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="0.0" width="50" height="33"/>
@ -848,13 +853,13 @@
</customSpacing>
</stackView>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="QuB-jf-wmu">
<rect key="frame" x="20" y="176" width="457" height="5"/>
<rect key="frame" x="20" y="305" width="457" height="5"/>
</box>
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CBs-1o-JjM">
<rect key="frame" x="20" y="80" width="250" height="82"/>
<rect key="frame" x="20" y="179" width="250" height="112"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3hp-BV-Ls3" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="60" width="250" height="22"/>
<rect key="frame" x="0.0" y="90" width="250" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="Billy Snedden" placeholderString="Licensee Name" drawsBackground="YES" id="I01-Tl-E8r">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -869,6 +874,25 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VLP-0u-Kf3" customClass="DSFFloatLabelledSecureTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="60" width="250" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="caterpillar" placeholderString="Password" drawsBackground="YES" id="2oD-a4-pQh">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="12"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="placeholderSpacing">
<real key="value" value="1"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="IaQ-aC-gRR"/>
</connections>
</textField>
<textField horizontalHuggingPriority="100" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MfQ-T7-eym" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="30" width="250" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="1DRF-1X" placeholderString="Serial Number" drawsBackground="YES" id="3Xv-o0-6Io">
@ -903,23 +927,27 @@
</textField>
</subviews>
<constraints>
<constraint firstItem="VLP-0u-Kf3" firstAttribute="leading" secondItem="3hp-BV-Ls3" secondAttribute="leading" id="1tz-ab-IsB"/>
<constraint firstAttribute="trailing" secondItem="3hp-BV-Ls3" secondAttribute="trailing" id="Cxv-bf-SFN"/>
<constraint firstItem="ueu-5M-hVI" firstAttribute="leading" secondItem="3hp-BV-Ls3" secondAttribute="leading" id="HGs-7T-sEb"/>
<constraint firstAttribute="width" constant="250" id="SfX-Wt-05r"/>
<constraint firstItem="MfQ-T7-eym" firstAttribute="leading" secondItem="3hp-BV-Ls3" secondAttribute="leading" id="Xpi-7I-DrD"/>
<constraint firstItem="MfQ-T7-eym" firstAttribute="trailing" secondItem="3hp-BV-Ls3" secondAttribute="trailing" id="YEC-So-F7G"/>
<constraint firstItem="ueu-5M-hVI" firstAttribute="trailing" secondItem="3hp-BV-Ls3" secondAttribute="trailing" id="gq8-B3-sya"/>
<constraint firstItem="VLP-0u-Kf3" firstAttribute="trailing" secondItem="3hp-BV-Ls3" secondAttribute="trailing" id="lxu-7C-U86"/>
<constraint firstItem="3hp-BV-Ls3" firstAttribute="leading" secondItem="CBs-1o-JjM" secondAttribute="leading" id="w8m-Ox-vCH"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
@ -943,7 +971,7 @@
<constraint firstItem="pnz-8U-z2F" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="zgF-Fx-OjE"/>
</constraints>
</view>
<point key="canvasLocation" x="-163.5" y="191.5"/>
<point key="canvasLocation" x="-480.5" y="144"/>
</window>
</objects>
</document>

View File

@ -0,0 +1,456 @@
/* Class = "NSMenuItem"; title = "Customize Toolbar…"; ObjectID = "1UK-8n-QPP"; */
"1UK-8n-QPP.title" = "Customize Toolbar…";
/* Class = "NSMenuItem"; title = "DSFFloatLabelledTextControls"; ObjectID = "1Xt-HY-uBw"; */
"1Xt-HY-uBw.title" = "DSFFloatLabelledTextControls";
/* Class = "NSMenu"; title = "Find"; ObjectID = "1b7-l0-nxx"; */
"1b7-l0-nxx.title" = "Find";
/* Class = "NSMenuItem"; title = "Lower"; ObjectID = "1tx-W0-xDw"; */
"1tx-W0-xDw.title" = "Lower";
/* Class = "NSMenuItem"; title = "Raise"; ObjectID = "2h7-ER-AoG"; */
"2h7-ER-AoG.title" = "Raise";
/* Class = "NSTextFieldCell"; placeholderString = "Password"; ObjectID = "2oD-a4-pQh"; */
"2oD-a4-pQh.placeholderString" = "كلمه السر";
/* Class = "NSTextFieldCell"; title = "caterpillar"; ObjectID = "2oD-a4-pQh"; */
"2oD-a4-pQh.title" = "caterpillar";
/* Class = "NSMenuItem"; title = "Transformations"; ObjectID = "2oI-Rn-ZJC"; */
"2oI-Rn-ZJC.title" = "Transformations";
/* Class = "NSMenu"; title = "Spelling"; ObjectID = "3IN-sU-3Bg"; */
"3IN-sU-3Bg.title" = "Spelling";
/* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "3Om-Ey-2VK"; */
"3Om-Ey-2VK.title" = "Use Default";
/* Class = "NSTextFieldCell"; placeholderString = "Serial Number"; ObjectID = "3Xv-o0-6Io"; */
"3Xv-o0-6Io.placeholderString" = "رقم سري";
/* Class = "NSTextFieldCell"; title = "1DRF-1X"; ObjectID = "3Xv-o0-6Io"; */
"3Xv-o0-6Io.title" = "1DRF-1X";
/* Class = "NSMenu"; title = "Speech"; ObjectID = "3rS-ZA-NoH"; */
"3rS-ZA-NoH.title" = "Speech";
/* Class = "NSMenuItem"; title = "Tighten"; ObjectID = "46P-cB-AYj"; */
"46P-cB-AYj.title" = "Tighten";
/* Class = "NSMenuItem"; title = "Find"; ObjectID = "4EN-yA-p0u"; */
"4EN-yA-p0u.title" = "Find";
/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "4J7-dP-txa"; */
"4J7-dP-txa.title" = "Enter Full Screen";
/* Class = "NSMenuItem"; title = "Quit DSFFloatLabelledTextControls"; ObjectID = "4sb-4s-VLi"; */
"4sb-4s-VLi.title" = "Quit DSFFloatLabelledTextControls";
/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "5QF-Oa-p0T"; */
"5QF-Oa-p0T.title" = "Edit";
/* Class = "NSMenuItem"; title = "Copy Style"; ObjectID = "5Vv-lz-BsD"; */
"5Vv-lz-BsD.title" = "Copy Style";
/* Class = "NSTextFieldCell"; placeholderString = "First Name"; ObjectID = "5Wb-1R-5YV"; */
"5Wb-1R-5YV.placeholderString" = "الاسم الاول";
/* Class = "NSTextFieldCell"; title = "Billy"; ObjectID = "5Wb-1R-5YV"; */
"5Wb-1R-5YV.title" = "Billy";
/* Class = "NSMenuItem"; title = "About DSFFloatLabelledTextControls"; ObjectID = "5kV-Vb-QxS"; */
"5kV-Vb-QxS.title" = "About DSFFloatLabelledTextControls";
/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "6dh-zS-Vam"; */
"6dh-zS-Vam.title" = "Redo";
/* Class = "NSTextFieldCell"; placeholderString = "Last Name"; ObjectID = "74P-hN-0Vg"; */
"74P-hN-0Vg.placeholderString" = "الكنية";
/* Class = "NSTextFieldCell"; title = "Snedden"; ObjectID = "74P-hN-0Vg"; */
"74P-hN-0Vg.title" = "Snedden";
/* Class = "NSMenuItem"; title = "Correct Spelling Automatically"; ObjectID = "78Y-hA-62v"; */
"78Y-hA-62v.title" = "Correct Spelling Automatically";
/* Class = "NSMenu"; title = "Writing Direction"; ObjectID = "8mr-sm-Yjd"; */
"8mr-sm-Yjd.title" = "Writing Direction";
/* Class = "NSMenuItem"; title = "Substitutions"; ObjectID = "9ic-FL-obx"; */
"9ic-FL-obx.title" = "Substitutions";
/* Class = "NSMenuItem"; title = "Smart Copy/Paste"; ObjectID = "9yt-4B-nSM"; */
"9yt-4B-nSM.title" = "Smart Copy/Paste";
/* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */
"AYu-sK-qS6.title" = "Main Menu";
/* Class = "NSTextFieldCell"; placeholderString = "Specific Location (Optional)"; ObjectID = "BHQ-MA-tIJ"; */
"BHQ-MA-tIJ.placeholderString" = "موقع محدد (اختياري)";
/* Class = "NSMenuItem"; title = "Preferences…"; ObjectID = "BOF-NM-1cW"; */
"BOF-NM-1cW.title" = "Preferences…";
/* Class = "NSMenuItem"; title = "\tLeft to Right"; ObjectID = "BgM-ve-c93"; */
"BgM-ve-c93.title" = "\tLeft to Right";
/* Class = "NSMenuItem"; title = "Save As…"; ObjectID = "Bw7-FT-i3A"; */
"Bw7-FT-i3A.title" = "Save As…";
/* Class = "NSMenuItem"; title = "Close"; ObjectID = "DVo-aG-piG"; */
"DVo-aG-piG.title" = "Close";
/* Class = "NSMenuItem"; title = "Spelling and Grammar"; ObjectID = "Dv1-io-Yv7"; */
"Dv1-io-Yv7.title" = "Spelling and Grammar";
/* Class = "NSTextFieldCell"; placeholderString = "Title"; ObjectID = "ETD-Tb-a7N"; */
"ETD-Tb-a7N.placeholderString" = "لقب";
/* Class = "NSTextFieldCell"; title = "Groovy Software v1"; ObjectID = "ETD-Tb-a7N"; */
"ETD-Tb-a7N.title" = "Groovy Software v1";
/* Class = "NSMenu"; title = "Help"; ObjectID = "F2S-fz-NVQ"; */
"F2S-fz-NVQ.title" = "Help";
/* Class = "NSMenuItem"; title = "DSFFloatLabelledTextControls Help"; ObjectID = "FKE-Sm-Kum"; */
"FKE-Sm-Kum.title" = "DSFFloatLabelledTextControls Help";
/* Class = "NSMenuItem"; title = "Text"; ObjectID = "Fal-I4-PZk"; */
"Fal-I4-PZk.title" = "Text";
/* Class = "NSMenu"; title = "Substitutions"; ObjectID = "FeM-D8-WVr"; */
"FeM-D8-WVr.title" = "Substitutions";
/* Class = "NSMenuItem"; title = "Bold"; ObjectID = "GB9-OM-e27"; */
"GB9-OM-e27.title" = "Bold";
/* Class = "NSMenu"; title = "Format"; ObjectID = "GEO-Iw-cKr"; */
"GEO-Iw-cKr.title" = "Format";
/* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "GUa-eO-cwY"; */
"GUa-eO-cwY.title" = "Use Default";
/* Class = "NSMenuItem"; title = "Font"; ObjectID = "Gi5-1S-RQB"; */
"Gi5-1S-RQB.title" = "Font";
/* Class = "NSMenuItem"; title = "Writing Direction"; ObjectID = "H1b-Si-o9J"; */
"H1b-Si-o9J.title" = "Writing Direction";
/* Class = "NSMenuItem"; title = "View"; ObjectID = "H8h-7b-M4v"; */
"H8h-7b-M4v.title" = "View";
/* Class = "NSMenuItem"; title = "Text Replacement"; ObjectID = "HFQ-gK-NFA"; */
"HFQ-gK-NFA.title" = "Text Replacement";
/* Class = "NSMenuItem"; title = "Show Spelling and Grammar"; ObjectID = "HFo-cy-zxI"; */
"HFo-cy-zxI.title" = "Show Spelling and Grammar";
/* Class = "NSMenu"; title = "View"; ObjectID = "HyV-fh-RgO"; */
"HyV-fh-RgO.title" = "View";
/* Class = "NSTextFieldCell"; placeholderString = "Licensee Name"; ObjectID = "I01-Tl-E8r"; */
"I01-Tl-E8r.placeholderString" = "اسم المرخص له";
/* Class = "NSTextFieldCell"; title = "Billy Snedden"; ObjectID = "I01-Tl-E8r"; */
"I01-Tl-E8r.title" = "Billy Snedden";
/* Class = "NSMenuItem"; title = "Subscript"; ObjectID = "I0S-gh-46l"; */
"I0S-gh-46l.title" = "Subscript";
/* Class = "NSMenuItem"; title = "Open…"; ObjectID = "IAo-SY-fd9"; */
"IAo-SY-fd9.title" = "Open…";
/* Class = "NSMenuItem"; title = "Justify"; ObjectID = "J5U-5w-g23"; */
"J5U-5w-g23.title" = "Justify";
/* Class = "NSMenuItem"; title = "Use None"; ObjectID = "J7y-lM-qPV"; */
"J7y-lM-qPV.title" = "Use None";
/* Class = "NSMenuItem"; title = "Revert to Saved"; ObjectID = "KaW-ft-85H"; */
"KaW-ft-85H.title" = "Revert to Saved";
/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "Kd2-mp-pUS"; */
"Kd2-mp-pUS.title" = "Show All";
/* Class = "NSMenuItem"; title = "Bring All to Front"; ObjectID = "LE2-aR-0XJ"; */
"LE2-aR-0XJ.title" = "Bring All to Front";
/* Class = "NSMenuItem"; title = "Paste Ruler"; ObjectID = "LVM-kO-fVI"; */
"LVM-kO-fVI.title" = "Paste Ruler";
/* Class = "NSMenuItem"; title = "\tLeft to Right"; ObjectID = "Lbh-J2-qVU"; */
"Lbh-J2-qVU.title" = "\tLeft to Right";
/* Class = "NSMenuItem"; title = "Copy Ruler"; ObjectID = "MkV-Pr-PK5"; */
"MkV-Pr-PK5.title" = "Copy Ruler";
/* Class = "NSMenuItem"; title = "Services"; ObjectID = "NMo-om-nkz"; */
"NMo-om-nkz.title" = "Services";
/* Class = "NSMenuItem"; title = "\tDefault"; ObjectID = "Nop-cj-93Q"; */
"Nop-cj-93Q.title" = "\tDefault";
/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "OY7-WF-poV"; */
"OY7-WF-poV.title" = "Minimize";
/* Class = "NSMenuItem"; title = "Baseline"; ObjectID = "OaQ-X3-Vso"; */
"OaQ-X3-Vso.title" = "Baseline";
/* Class = "NSMenuItem"; title = "Hide DSFFloatLabelledTextControls"; ObjectID = "Olw-nP-bQN"; */
"Olw-nP-bQN.title" = "Hide DSFFloatLabelledTextControls";
/* Class = "NSMenuItem"; title = "Find Previous"; ObjectID = "OwM-mh-QMV"; */
"OwM-mh-QMV.title" = "Find Previous";
/* Class = "NSMenuItem"; title = "Stop Speaking"; ObjectID = "Oyz-dy-DGm"; */
"Oyz-dy-DGm.title" = "Stop Speaking";
/* Class = "NSMenuItem"; title = "Bigger"; ObjectID = "Ptp-SP-VEL"; */
"Ptp-SP-VEL.title" = "Bigger";
/* Class = "NSMenuItem"; title = "Show Fonts"; ObjectID = "Q5e-8K-NDq"; */
"Q5e-8K-NDq.title" = "Show Fonts";
/* Class = "NSWindow"; title = "Floating Labels Test"; ObjectID = "QvC-M9-y7g"; */
"QvC-M9-y7g.title" = "Floating Labels Test";
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "R4o-n2-Eq4"; */
"R4o-n2-Eq4.title" = "Zoom";
/* Class = "NSMenuItem"; title = "\tRight to Left"; ObjectID = "RB4-Sm-HuC"; */
"RB4-Sm-HuC.title" = "\tRight to Left";
/* Class = "NSMenuItem"; title = "Superscript"; ObjectID = "Rqc-34-cIF"; */
"Rqc-34-cIF.title" = "Superscript";
/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "Ruw-6m-B2m"; */
"Ruw-6m-B2m.title" = "Select All";
/* Class = "NSMenuItem"; title = "Jump to Selection"; ObjectID = "S0p-oC-mLd"; */
"S0p-oC-mLd.title" = "Jump to Selection";
/* Class = "NSMenu"; title = "Window"; ObjectID = "Td7-aD-5lo"; */
"Td7-aD-5lo.title" = "Window";
/* Class = "NSMenuItem"; title = "Capitalize"; ObjectID = "UEZ-Bs-lqG"; */
"UEZ-Bs-lqG.title" = "Capitalize";
/* Class = "NSMenuItem"; title = "Center"; ObjectID = "VIY-Ag-zcb"; */
"VIY-Ag-zcb.title" = "Center";
/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "Vdr-fp-XzO"; */
"Vdr-fp-XzO.title" = "Hide Others";
/* Class = "NSMenuItem"; title = "Italic"; ObjectID = "Vjx-xi-njq"; */
"Vjx-xi-njq.title" = "Italic";
/* Class = "NSMenu"; title = "Edit"; ObjectID = "W48-6f-4Dl"; */
"W48-6f-4Dl.title" = "Edit";
/* Class = "NSMenuItem"; title = "Underline"; ObjectID = "WRG-CD-K1S"; */
"WRG-CD-K1S.title" = "Underline";
/* Class = "NSMenuItem"; title = "New"; ObjectID = "Was-JA-tGl"; */
"Was-JA-tGl.title" = "New";
/* Class = "NSMenuItem"; title = "Paste and Match Style"; ObjectID = "WeT-3V-zwk"; */
"WeT-3V-zwk.title" = "Paste and Match Style";
/* Class = "NSTextFieldCell"; placeholderString = "Description"; ObjectID = "XtC-Dr-5RL"; */
"XtC-Dr-5RL.placeholderString" = "وصف";
/* Class = "NSMenuItem"; title = "Find…"; ObjectID = "Xz5-n4-O0W"; */
"Xz5-n4-O0W.title" = "Find…";
/* Class = "NSMenuItem"; title = "Find and Replace…"; ObjectID = "YEy-JH-Tfz"; */
"YEy-JH-Tfz.title" = "Find and Replace…";
/* Class = "NSMenuItem"; title = "\tDefault"; ObjectID = "YGs-j5-SAR"; */
"YGs-j5-SAR.title" = "\tDefault";
/* Class = "NSMenuItem"; title = "Start Speaking"; ObjectID = "Ynk-f8-cLZ"; */
"Ynk-f8-cLZ.title" = "Start Speaking";
/* Class = "NSMenuItem"; title = "Align Left"; ObjectID = "ZM1-6Q-yy1"; */
"ZM1-6Q-yy1.title" = "Align Left";
/* Class = "NSMenuItem"; title = "Paragraph"; ObjectID = "ZvO-Gk-QUH"; */
"ZvO-Gk-QUH.title" = "Paragraph";
/* Class = "NSMenuItem"; title = "Print…"; ObjectID = "aTl-1u-JFS"; */
"aTl-1u-JFS.title" = "Print…";
/* Class = "NSMenuItem"; title = "Window"; ObjectID = "aUF-d1-5bR"; */
"aUF-d1-5bR.title" = "Window";
/* Class = "NSMenu"; title = "Font"; ObjectID = "aXa-aM-Jaq"; */
"aXa-aM-Jaq.title" = "Font";
/* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "agt-UL-0e3"; */
"agt-UL-0e3.title" = "Use Default";
/* Class = "NSMenuItem"; title = "Show Colors"; ObjectID = "bgn-CT-cEk"; */
"bgn-CT-cEk.title" = "Show Colors";
/* Class = "NSMenu"; title = "File"; ObjectID = "bib-Uj-vzu"; */
"bib-Uj-vzu.title" = "File";
/* Class = "NSMenuItem"; title = "Use Selection for Find"; ObjectID = "buJ-ug-pKt"; */
"buJ-ug-pKt.title" = "Use Selection for Find";
/* Class = "NSMenu"; title = "Transformations"; ObjectID = "c8a-y6-VQd"; */
"c8a-y6-VQd.title" = "Transformations";
/* Class = "NSMenuItem"; title = "Use None"; ObjectID = "cDB-IK-hbR"; */
"cDB-IK-hbR.title" = "Use None";
/* Class = "NSMenuItem"; title = "Selection"; ObjectID = "cqv-fj-IhA"; */
"cqv-fj-IhA.title" = "Selection";
/* Class = "NSMenuItem"; title = "Smart Links"; ObjectID = "cwL-P1-jid"; */
"cwL-P1-jid.title" = "Smart Links";
/* Class = "NSMenuItem"; title = "Make Lower Case"; ObjectID = "d9M-CD-aMd"; */
"d9M-CD-aMd.title" = "Make Lower Case";
/* Class = "NSMenu"; title = "Text"; ObjectID = "d9c-me-L2H"; */
"d9c-me-L2H.title" = "Text";
/* Class = "NSMenuItem"; title = "File"; ObjectID = "dMs-cI-mzQ"; */
"dMs-cI-mzQ.title" = "File";
/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "dRJ-4n-Yzg"; */
"dRJ-4n-Yzg.title" = "Undo";
/* Class = "NSTextFieldCell"; placeholderString = "Price"; ObjectID = "doO-uJ-2o2"; */
"doO-uJ-2o2.placeholderString" = "السعر";
/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "gVA-U4-sdL"; */
"gVA-U4-sdL.title" = "Paste";
/* Class = "NSMenuItem"; title = "Smart Quotes"; ObjectID = "hQb-2v-fYv"; */
"hQb-2v-fYv.title" = "Smart Quotes";
/* Class = "NSMenuItem"; title = "Check Document Now"; ObjectID = "hz2-CU-CR7"; */
"hz2-CU-CR7.title" = "Check Document Now";
/* Class = "NSMenu"; title = "Services"; ObjectID = "hz9-B4-Xy5"; */
"hz9-B4-Xy5.title" = "Services";
/* Class = "NSMenuItem"; title = "Smaller"; ObjectID = "i1d-Er-qST"; */
"i1d-Er-qST.title" = "Smaller";
/* Class = "NSMenu"; title = "Baseline"; ObjectID = "ijk-EB-dga"; */
"ijk-EB-dga.title" = "Baseline";
/* Class = "NSMenuItem"; title = "Kern"; ObjectID = "jBQ-r6-VK2"; */
"jBQ-r6-VK2.title" = "Kern";
/* Class = "NSMenuItem"; title = "\tRight to Left"; ObjectID = "jFq-tB-4Kx"; */
"jFq-tB-4Kx.title" = "\tRight to Left";
/* Class = "NSMenuItem"; title = "Format"; ObjectID = "jxT-CU-nIS"; */
"jxT-CU-nIS.title" = "Format";
/* Class = "NSMenuItem"; title = "Show Sidebar"; ObjectID = "kIP-vf-haE"; */
"kIP-vf-haE.title" = "Show Sidebar";
/* Class = "NSTextFieldCell"; placeholderString = "Maximum Clients"; ObjectID = "l0d-Jv-tQ6"; */
"l0d-Jv-tQ6.placeholderString" = "أقصى عدد من العملاء";
/* Class = "NSTextFieldCell"; title = "2"; ObjectID = "l0d-Jv-tQ6"; */
"l0d-Jv-tQ6.title" = "2";
/* Class = "NSMenuItem"; title = "Check Grammar With Spelling"; ObjectID = "mK6-2p-4JG"; */
"mK6-2p-4JG.title" = "Check Grammar With Spelling";
/* Class = "NSButton"; ibShadowedToolTip = "Check for programattic changing of stringValue"; ObjectID = "nTi-Ym-3BC"; */
"nTi-Ym-3BC.ibShadowedToolTip" = "Check for programattic changing of stringValue";
/* Class = "NSMenuItem"; title = "Ligatures"; ObjectID = "o6e-r0-MWq"; */
"o6e-r0-MWq.title" = "Ligatures";
/* Class = "NSMenu"; title = "Open Recent"; ObjectID = "oas-Oc-fiZ"; */
"oas-Oc-fiZ.title" = "Open Recent";
/* Class = "NSMenuItem"; title = "Loosen"; ObjectID = "ogc-rX-tC1"; */
"ogc-rX-tC1.title" = "Loosen";
/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "pTp-yr-DKx"; */
"pTp-yr-DKx.title" = "أعاد التشغيل";
/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "pa3-QI-u2k"; */
"pa3-QI-u2k.title" = "Delete";
/* Class = "NSBox"; title = "Purchase Form"; ObjectID = "pnz-8U-z2F"; */
"pnz-8U-z2F.title" = "Purchase Form";
/* Class = "NSMenuItem"; title = "Save…"; ObjectID = "pxx-59-PXV"; */
"pxx-59-PXV.title" = "Save…";
/* Class = "NSMenuItem"; title = "Find Next"; ObjectID = "q09-fT-Sye"; */
"q09-fT-Sye.title" = "Find Next";
/* Class = "NSMenuItem"; title = "Page Setup…"; ObjectID = "qIS-W8-SiK"; */
"qIS-W8-SiK.title" = "Page Setup…";
/* Class = "NSMenuItem"; title = "Check Spelling While Typing"; ObjectID = "rbD-Rh-wIN"; */
"rbD-Rh-wIN.title" = "Check Spelling While Typing";
/* Class = "NSMenuItem"; title = "Smart Dashes"; ObjectID = "rgM-f4-ycn"; */
"rgM-f4-ycn.title" = "Smart Dashes";
/* Class = "NSMenuItem"; title = "Show Toolbar"; ObjectID = "snW-S8-Cw5"; */
"snW-S8-Cw5.title" = "Show Toolbar";
/* Class = "NSMenuItem"; title = "Data Detectors"; ObjectID = "tRr-pd-1PS"; */
"tRr-pd-1PS.title" = "Data Detectors";
/* Class = "NSMenuItem"; title = "Open Recent"; ObjectID = "tXI-mr-wws"; */
"tXI-mr-wws.title" = "Open Recent";
/* Class = "NSMenu"; title = "Kern"; ObjectID = "tlD-Oa-oAM"; */
"tlD-Oa-oAM.title" = "Kern";
/* Class = "NSMenu"; title = "DSFFloatLabelledTextControls"; ObjectID = "uQy-DD-JDr"; */
"uQy-DD-JDr.title" = "DSFFloatLabelledTextControls";
/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */
"uRl-iY-unG.title" = "Cut";
/* Class = "NSMenuItem"; title = "Paste Style"; ObjectID = "vKC-jM-MkH"; */
"vKC-jM-MkH.title" = "Paste Style";
/* Class = "NSMenuItem"; title = "Show Ruler"; ObjectID = "vLm-3I-IUL"; */
"vLm-3I-IUL.title" = "Show Ruler";
/* Class = "NSMenuItem"; title = "Clear Menu"; ObjectID = "vNY-rz-j42"; */
"vNY-rz-j42.title" = "Clear Menu";
/* Class = "NSMenuItem"; title = "Make Upper Case"; ObjectID = "vmV-6d-7jI"; */
"vmV-6d-7jI.title" = "Make Upper Case";
/* Class = "NSMenu"; title = "Ligatures"; ObjectID = "w0m-vy-SC9"; */
"w0m-vy-SC9.title" = "Ligatures";
/* Class = "NSMenuItem"; title = "Align Right"; ObjectID = "wb2-vD-lq4"; */
"wb2-vD-lq4.title" = "Align Right";
/* Class = "NSMenuItem"; title = "Help"; ObjectID = "wpr-3q-Mcd"; */
"wpr-3q-Mcd.title" = "Help";
/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "x3v-GG-iWU"; */
"x3v-GG-iWU.title" = "Copy";
/* Class = "NSMenuItem"; title = "Use All"; ObjectID = "xQD-1f-W4t"; */
"xQD-1f-W4t.title" = "Use All";
/* Class = "NSMenuItem"; title = "Speech"; ObjectID = "xrE-MZ-jX0"; */
"xrE-MZ-jX0.title" = "Speech";
/* Class = "NSMenuItem"; title = "Show Substitutions"; ObjectID = "z6F-FW-3nz"; */
"z6F-FW-3nz.title" = "Show Substitutions";

View File

@ -1,29 +1,23 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version: 5.4
import PackageDescription
let package = Package(
name: "DSFFloatLabelledTextField",
platforms: [
.macOS(.v10_11)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "DSFFloatLabelledTextField",
targets: ["DSFFloatLabelledTextField"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "DSFFloatLabelledTextField",
dependencies: [])
],
swiftLanguageVersions: [.v5]
name: "DSFFloatLabelledTextField",
platforms: [
.macOS(.v10_11)
],
products: [
.library(
name: "DSFFloatLabelledTextField",
targets: ["DSFFloatLabelledTextField"]
),
],
targets: [
.target(
name: "DSFFloatLabelledTextField",
dependencies: []
)
],
swiftLanguageVersions: [.v5]
)

View File

@ -1,8 +1,9 @@
# macOS Float Label Pattern Text Field
A macOS Cocoa single-line NSTextField that implements the Float Label Pattern. You can read about the float pattern [here](http://mds.is/float-label-pattern/). Usable in Swift and Objective-C projects.
A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern.
You can read about the float pattern [here](http://mds.is/float-label-pattern/).
![](https://img.shields.io/github/v/tag/dagronf/DSFFloatLabelledTextControl) ![](https://img.shields.io/badge/macOS-10.11+-red) ![](https://img.shields.io/badge/Swift-5.0-orange.svg)
![](https://img.shields.io/github/v/tag/dagronf/DSFFloatLabelledTextControl) ![](https://img.shields.io/badge/macOS-10.11+-red) ![](https://img.shields.io/badge/Swift-5.4-orange.svg) ![](https://img.shields.io/badge/ObjectiveC-2.0-purple.svg)
![](https://img.shields.io/badge/License-MIT-lightgrey) [![](https://img.shields.io/badge/pod-compatible-informational)](https://cocoapods.org) [![](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager)
## Screenshot
@ -23,14 +24,12 @@ Add `https://github.com/dagronf/DSFFloatLabelledTextControl` to your project.
Add `pod 'DSFFloatLabelledTextControl', :git => 'https://github.com/dagronf/DSFFloatLabelledTextControl'` to your podfile.
### Directly
Copy the `DSFFloatLabelledTextField.swift` into your project. This class inherits directly from `NSTextField` so all settings that you make on a text field (eg. alignment) should also apply here.
## Usage
### Interface builder
* Drop in a new Text Field into your canvas and set its class to `DSFFloatLabelledTextField`
* Drop in a new Text Field into your canvas and set its class to `DSFFloatLabelledTextField` or
`DSFFloatLabelledSecureTextControl` depending on whether you need a secure field
* Set the size and style of your primary font as you would a regular text field
* Set the size of the secondary font via the attributes inspector for the control
@ -47,11 +46,18 @@ parentView.addSubview(field)
## Custom Properties
This control inherits from `NSTextField`, so all `NSTextField` functionalities (like cocoa binding and Interface Builder settings) are available. If you can use an NSTextField, you can use this control.
These controls inherit from `NSTextField`, so all `NSTextField` functionalities (like cocoa binding and Interface Builder settings) are available. If you can use an NSTextField, you can use this control.
`placeholderTextSize` - the size of the text used in the floating label (in pt)
* `placeholderTextSize` - the size of the text used in the floating label (in pt)
* `placeholderSpacing` - the distance between the text field text and the floating label (in px)
`placeholderSpacing` - the distance between the text field text and the floating label (in px)
## Delegate Handling
You can specify a delegate (`floatLabelDelegate`), either programatically or via Interface Builder, to receive additional information regarding the actions of the control.
## Screenshot
<img src="https://github.com/dagronf/dagronf.github.io/blob/master/art/projects/DSFFloatingLabel/light-mode-secure-field.png?raw=true" alt="drawing" width="265"/>
## Credits
@ -59,12 +65,17 @@ This control inherits from `NSTextField`, so all `NSTextField` functionalities (
* Read about the pattern [here](http://mds.is/float-label-pattern/)
* [UITextField implementation](https://github.com/jverdi/JVFloatLabeledTextField)
## Versions
* `2.0.0`: Moved secure text field into separate class (fixing security warnings in modern Xcode)
* `1.8.0`: Added secure text field support, delegate support
## License
```
MIT License
Copyright (c) 2020 Darren Ford
Copyright (c) 2023 Darren Ford
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,448 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelledSecureTextField.swift
// (generated using tools/regenerate-sources.sh)
//
// Copyright © 2023 Darren Ford. All rights reserved.
//
// MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import AppKit
/// DSFFloatLabelledTextField delegate protocol
@objc public protocol DSFFloatLabelledSecureTextFieldDelegate: NSObjectProtocol {
/// Called when the label is shown or hidden
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledSecureTextField, didShowFloatingLabel didShow: Bool)
/// Called when the field becomes or loses first responder status
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledSecureTextField, didFocus: Bool)
/// Called when the content of the field changes
@objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelledSecureTextField)
}
/// An NSSecureTextField that implements the Float Label Pattern
@IBDesignable open class DSFFloatLabelledSecureTextField: NSSecureTextField {
/// Optional delegate to provide callbacks for the floating label state.
///
/// This delegate can be set via Interface Builder or programatically
@IBOutlet @objc public weak var floatLabelDelegate: DSFFloatLabelledSecureTextFieldDelegate?
/// The size (in pt) of the floating label text
@IBInspectable public var placeholderTextSize: CGFloat = NSFont.smallSystemFontSize {
didSet {
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.reconfigureControl()
}
}
/// Spacing between the floating label and the text field text
@IBInspectable public var placeholderSpacing: CGFloat = 0.0 {
didSet {
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.reconfigureControl()
}
}
// Override so that we can notify when the developer changes the text programatically too
open override var stringValue: String {
get {
return super.stringValue
}
set {
super.stringValue = newValue
NotificationCenter.default.post(name: NSControl.textDidChangeNotification, object: self)
}
}
// Override so that we can update when the developer changes the placeholder string
open override var placeholderString: String? {
get {
return super.placeholderString
}
set {
super.placeholderString = newValue
self.floatingLabel.stringValue = newValue ?? ""
}
}
/// Floating label
private let floatingLabel = NSTextField()
/// Is the label currently showing
private var isShowing: Bool = false {
didSet {
self.floatLabelDelegate?.floatLabelledTextField?(self, didShowFloatingLabel: self.isShowing)
}
}
/// Constraint to tie the label to the top of the control
private var floatingTop: NSLayoutConstraint?
/// Height of the control
private var heightConstraint: NSLayoutConstraint?
/// Observers for the font and placeholder text
private var fontObserver: NSKeyValueObservation?
private var placeholderObserver: NSKeyValueObservation?
/// Returns the height of the placeholder text
var placeholderHeight: CGFloat {
let layoutManager = NSLayoutManager()
return layoutManager.defaultLineHeight(for: self.floatingLabel.font!) + 1
}
/// Returns the height of the primary (editable) text
private var textHeight: CGFloat {
let layoutManager = NSLayoutManager()
return layoutManager.defaultLineHeight(for: self.font!) + 1
}
/// Returns the total height of the control given the font settings
private var controlHeight: CGFloat {
return self.textHeight + self.placeholderSpacing + self.placeholderHeight
}
open override var intrinsicContentSize: NSSize {
var sz = super.intrinsicContentSize
sz.height = self.controlHeight
return sz
}
func configureCell() {
let customCell = DSFFloatLabelledSecureTextFieldCell()
customCell.isEditable = true
customCell.wraps = false
customCell.usesSingleLineMode = true
customCell.placeholderString = self.placeholderString
customCell.title = self.stringValue
customCell.font = self.font
customCell.isBordered = self.isBordered
customCell.isBezeled = self.isBezeled
customCell.bezelStyle = self.bezelStyle
customCell.isScrollable = true
customCell.isContinuous = self.isContinuous
customCell.alignment = self.alignment
customCell.formatter = self.formatter
customCell.topOffset = self.placeholderHeight
self.cell = customCell
}
func setTopOffset(_ value: CGFloat) {
guard var f = self.cell as? DSFFloatLabelledTextFieldCellProtocol else {
return
}
f.topOffset = value
}
/// Set the fonts to be used in the control
open func setFonts(primary: NSFont, secondary: NSFont) {
self.floatingLabel.font = secondary
self.font = primary
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.setup()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
}
open override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
// When we're added to the view, make sure that the floating label alignment matches us
// (as the user might have changed the alignment BEFORE adding to the superview
self.floatingLabel.alignment = self.alignment
}
private func setup() {
// Setup the common elements of the control
self.commonSetup()
// Configure a default text cell
self.configureCell()
// Listen to changes in the primary font so we can reconfigure to match
self.fontObserver = self.observe(\.font, options: [.new]) { [weak self] _, _ in
self?.reconfigureControl()
}
// Listen to changes in the placeholder text so we can reflect it in the floater
self.placeholderObserver = self.observe(\.placeholderString, options: [.new]) { [weak self] _, _ in
guard let `self` = self else { return }
self.floatingLabel.stringValue = self.placeholderString!
self.reconfigureControl()
}
}
/// Build the floating label
private func createFloatingLabel() {
if self.floatingLabel.superview == nil {
self.addSubview(self.floatingLabel)
}
self.floatingLabel.wantsLayer = true
self.floatingLabel.isEditable = false
self.floatingLabel.isSelectable = false
self.floatingLabel.isEnabled = true
self.floatingLabel.isBezeled = false
self.floatingLabel.isBordered = false
self.floatingLabel.translatesAutoresizingMaskIntoConstraints = false
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.floatingLabel.textColor = NSColor.placeholderTextColor
self.floatingLabel.stringValue = self.placeholderString ?? ""
self.floatingLabel.alphaValue = 0.0
self.floatingLabel.alignment = self.alignment
self.floatingLabel.drawsBackground = false
self.floatingTop = NSLayoutConstraint(
item: self.floatingLabel, attribute: .top,
relatedBy: .equal,
toItem: self, attribute: .top,
multiplier: 1.0, constant: 10
)
self.addConstraint(self.floatingTop!)
var x = NSLayoutConstraint(
item: self.floatingLabel, attribute: .leading,
relatedBy: .equal,
toItem: self, attribute: .leading,
multiplier: 1.0, constant: self.isBezeled ? 4 : 0
)
self.addConstraint(x)
x = NSLayoutConstraint(
item: self.floatingLabel, attribute: .trailing,
relatedBy: .equal,
toItem: self, attribute: .trailing,
multiplier: 1.0, constant: self.isBezeled ? -4 : 0
)
self.addConstraint(x)
self.floatingLabel.setContentHuggingPriority(NSLayoutConstraint.Priority(10), for: .horizontal)
self.floatingLabel.setContentCompressionResistancePriority(NSLayoutConstraint.Priority(10), for: .horizontal)
}
private func commonSetup() {
self.wantsLayer = true
self.translatesAutoresizingMaskIntoConstraints = false
self.usesSingleLineMode = true
self.delegate = self
// Default to natural layout
self.alignment = .natural
self.createFloatingLabel()
self.heightConstraint = NSLayoutConstraint(
item: self, attribute: .height,
relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute,
multiplier: 1.0, constant: self.controlHeight
)
self.addConstraint(self.heightConstraint!)
// If the field already has text, make sure the placeholder is shown
if self.stringValue.count > 0 {
self.showPlaceholder(animated: false)
}
}
/// Change the layout if any changes occur
private func reconfigureControl() {
if self.isCurrentFocus() {
/// If we are currently editing, then finish before changing.
self.window?.endEditing(for: nil)
}
self.expandFrame()
}
/// Rebuild the frame of the text field to match the new settings
private func expandFrame() {
self.heightConstraint?.constant = self.controlHeight
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
open override func layout() {
super.layout()
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
}
// MARK: - Focus and editing
extension DSFFloatLabelledSecureTextField: NSTextFieldDelegate {
// Change the floating label color to represent active state
private func setFloatingLabelActive(_ active: Bool) {
if active {
self.floatingLabel.textColor = NSColor.simulatedAccentColor
}
else {
self.floatingLabel.textColor = NSColor.placeholderTextColor
}
self.floatLabelDelegate?.floatLabelledTextField?(self, didFocus: active)
}
public func controlTextDidChange(_ obj: Notification) {
guard let field = obj.object as? NSTextField else {
return
}
if field.stringValue.count > 0, !self.isShowing {
self.showPlaceholder(animated: true)
}
else if field.stringValue.count == 0, self.isShowing {
self.hidePlaceholder()
}
self.floatLabelDelegate?.floatLabelledTextFieldContentChanged?(self)
}
open override func becomeFirstResponder() -> Bool {
let becomeResult = super.becomeFirstResponder()
if becomeResult, self.isCurrentFocus() {
// Set the color of the 'label' to match the current focus color.
// We need to perform this on the main thread after the current set of notifications are processed
// Why? We (occasionally) receive a 'controlTextDidEndEditing' message AFTER we receive a
// 'becomeFirstResponder'. I've read that this is related to the text field automatically selecting
// text when taking focus, but I haven't been able to verify this in any useful manner.
DispatchQueue.main.async { [weak self] in
if let `self` = self {
self.setFloatingLabelActive(true)
}
}
}
return becomeResult
}
open func controlTextDidEndEditing(_: Notification) {
// When we lose focus, set the label color back to the placeholder color
self.setFloatingLabelActive(false)
}
/// Does our text field currently have input focus?
private func isCurrentFocus() -> Bool {
// 1. Get the window's first responder
// 2. Check is has an active field editor
// 3. Is the delegate of the field editor us?
if let fr = self.window?.firstResponder as? NSTextView,
self.window?.fieldEditor(false, for: nil) != nil,
fr.delegate === self {
return true
}
return false
}
}
// MARK: - Animations
extension DSFFloatLabelledSecureTextField {
/// Duration of the fade in/out of the secondary label
private var animationDuration: TimeInterval {
return shouldAnimate() ? 0.4 : 0.0
}
/// Returns true if the system is NOT set to reduce motion (accessibility settings)
private func shouldAnimate() -> Bool {
if #available(OSX 10.12, *) {
return !NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
} else {
// Fallback on earlier versions. Just animate
return true
}
}
private func showPlaceholder(animated: Bool) {
self.isShowing = true
if animated {
NSAnimationContext.runAnimationGroup({ [weak self] context in
guard let `self` = self else { return }
context.allowsImplicitAnimation = true
context.duration = self.animationDuration
self.floatingTop?.constant = 0
self.floatingLabel.alphaValue = 1.0
self.layoutSubtreeIfNeeded()
}, completionHandler: {
//
})
}
else {
self.setFloatingLabelActive(false)
self.floatingTop?.constant = 0
self.floatingLabel.alphaValue = 1.0
}
}
private func hidePlaceholder() {
self.isShowing = false
let duration = self.animationDuration
NSAnimationContext.runAnimationGroup({ [weak self] context in
guard let `self` = self else { return }
context.allowsImplicitAnimation = true
context.duration = duration
self.floatingTop?.constant = self.textHeight / 1.5
self.floatingLabel.alphaValue = 0.0
self.layoutSubtreeIfNeeded()
}, completionHandler: {
//
})
}
}
// MARK: - Cell definition
private class DSFFloatLabelledSecureTextFieldCell: NSSecureTextFieldCell, DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat = 0
private func offset() -> CGFloat {
return self.topOffset - (self.isBezeled ? 5 : 1)
}
override func titleRect(forBounds rect: NSRect) -> NSRect {
return NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
}
override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) {
let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
super.edit(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, event: event)
}
override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) {
let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
super.select(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength)
}
override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
let insetRect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y + self.offset(), width: cellFrame.width, height: cellFrame.height)
super.drawInterior(withFrame: insetRect, in: controlView)
}
}

View File

@ -1,38 +0,0 @@
//
// DSFFloatLabelledTextControl.h
// DSFFloatLabelledTextControl
//
// Created by Darren Ford on 4/2/19.
// Copyright © 2020 Darren Ford. All rights reserved.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Cocoa/Cocoa.h>
//! Project version number for DSFFloatLabelledTextControl.
FOUNDATION_EXPORT double DSFFloatLabelledTextControlVersionNumber;
//! Project version string for DSFFloatLabelledTextControl.
FOUNDATION_EXPORT const unsigned char DSFFloatLabelledTextControlVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DSFFloatLabelledTextControl/PublicHeader.h>

View File

@ -1,34 +1,51 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelledTextField.swift
// DSFFloatLabelledTextControls
// (generated using tools/regenerate-sources.sh)
//
// Created by Darren Ford on 4/2/19.
// Copyright © 2020 Darren Ford. All rights reserved.
// Copyright © 2023 Darren Ford. All rights reserved.
//
// MIT License
// MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Cocoa
import AppKit
/// DSFFloatLabelledTextField delegate protocol
@objc public protocol DSFFloatLabelledTextFieldDelegate: NSObjectProtocol {
/// Called when the label is shown or hidden
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didShowFloatingLabel didShow: Bool)
/// Called when the field becomes or loses first responder status
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didFocus: Bool)
/// Called when the content of the field changes
@objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelledTextField)
}
/// An NSTextField that implements the Float Label Pattern
@IBDesignable open class DSFFloatLabelledTextField: NSTextField {
/// Optional delegate to provide callbacks for the floating label state.
///
/// This delegate can be set via Interface Builder or programatically
@IBOutlet @objc public weak var floatLabelDelegate: DSFFloatLabelledTextFieldDelegate?
/// The size (in pt) of the floating label text
@IBInspectable public var placeholderTextSize: CGFloat = NSFont.smallSystemFontSize {
didSet {
@ -71,7 +88,11 @@ import Cocoa
private let floatingLabel = NSTextField()
/// Is the label currently showing
private var isShowing: Bool = false
private var isShowing: Bool = false {
didSet {
self.floatLabelDelegate?.floatLabelledTextField?(self, didShowFloatingLabel: self.isShowing)
}
}
/// Constraint to tie the label to the top of the control
private var floatingTop: NSLayoutConstraint?
@ -100,27 +121,14 @@ import Cocoa
return self.textHeight + self.placeholderSpacing + self.placeholderHeight
}
/// Returns of the color for the floating text
private var floatTextColor: NSColor {
if #available(macOS 10.14, *) {
return NSColor.controlAccentColor
}
else {
return NSColor.headerTextColor
}
}
open override var intrinsicContentSize: NSSize {
var sz = super.intrinsicContentSize
sz.height = self.controlHeight
return sz
}
/// Return the custom cell type
fileprivate lazy var fieldCell: DSFFloatLabelledTextFieldCell = {
func configureCell() {
let customCell = DSFFloatLabelledTextFieldCell()
customCell.topOffset = self.placeholderHeight
customCell.isEditable = true
customCell.wraps = false
customCell.usesSingleLineMode = true
@ -134,11 +142,16 @@ import Cocoa
customCell.isContinuous = self.isContinuous
customCell.alignment = self.alignment
customCell.formatter = self.formatter
customCell.topOffset = self.placeholderHeight
self.cell = customCell
}
return customCell
}()
func setTopOffset(_ value: CGFloat) {
guard var f = self.cell as? DSFFloatLabelledTextFieldCellProtocol else {
return
}
f.topOffset = value
}
/// Set the fonts to be used in the control
open func setFonts(primary: NSFont, secondary: NSFont) {
@ -168,6 +181,9 @@ import Cocoa
// Setup the common elements of the control
self.commonSetup()
// Configure a default text cell
self.configureCell()
// Listen to changes in the primary font so we can reconfigure to match
self.fontObserver = self.observe(\.font, options: [.new]) { [weak self] _, _ in
self?.reconfigureControl()
@ -195,7 +211,7 @@ import Cocoa
self.floatingLabel.isBordered = false
self.floatingLabel.translatesAutoresizingMaskIntoConstraints = false
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.floatingLabel.textColor = self.floatTextColor
self.floatingLabel.textColor = NSColor.placeholderTextColor
self.floatingLabel.stringValue = self.placeholderString ?? ""
self.floatingLabel.alphaValue = 0.0
self.floatingLabel.alignment = self.alignment
@ -224,6 +240,9 @@ import Cocoa
multiplier: 1.0, constant: self.isBezeled ? -4 : 0
)
self.addConstraint(x)
self.floatingLabel.setContentHuggingPriority(NSLayoutConstraint.Priority(10), for: .horizontal)
self.floatingLabel.setContentCompressionResistancePriority(NSLayoutConstraint.Priority(10), for: .horizontal)
}
private func commonSetup() {
@ -257,17 +276,23 @@ import Cocoa
/// If we are currently editing, then finish before changing.
self.window?.endEditing(for: nil)
}
self.fieldCell.topOffset = self.placeholderHeight + self.placeholderSpacing
self.expandFrame()
self.needsLayout = true
}
/// Rebuild the frame of the text field to match the new settings
private func expandFrame() {
self.heightConstraint?.constant = self.controlHeight
self.fieldCell.topOffset = self.placeholderHeight + self.placeholderSpacing
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
open override func layout() {
super.layout()
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
}
// MARK: - Focus and editing
@ -277,11 +302,13 @@ extension DSFFloatLabelledTextField: NSTextFieldDelegate {
// Change the floating label color to represent active state
private func setFloatingLabelActive(_ active: Bool) {
if active {
self.floatingLabel.textColor = self.floatTextColor
self.floatingLabel.textColor = NSColor.simulatedAccentColor
}
else {
self.floatingLabel.textColor = NSColor.placeholderTextColor
}
self.floatLabelDelegate?.floatLabelledTextField?(self, didFocus: active)
}
public func controlTextDidChange(_ obj: Notification) {
@ -295,6 +322,8 @@ extension DSFFloatLabelledTextField: NSTextFieldDelegate {
else if field.stringValue.count == 0, self.isShowing {
self.hidePlaceholder()
}
self.floatLabelDelegate?.floatLabelledTextFieldContentChanged?(self)
}
open override func becomeFirstResponder() -> Bool {
@ -391,7 +420,7 @@ extension DSFFloatLabelledTextField {
// MARK: - Cell definition
private class DSFFloatLabelledTextFieldCell: NSTextFieldCell {
private class DSFFloatLabelledTextFieldCell: NSTextFieldCell, DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat = 0
private func offset() -> CGFloat {
@ -417,4 +446,3 @@ private class DSFFloatLabelledTextFieldCell: NSTextFieldCell {
super.drawInterior(withFrame: insetRect, in: controlView)
}
}

View File

@ -0,0 +1,448 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelled${field_type}Field.swift
// (generated using tools/regenerate-sources.sh)
//
// Copyright © 2023 Darren Ford. All rights reserved.
//
// MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import AppKit
/// DSFFloatLabelledTextField delegate protocol
@objc public protocol DSFFloatLabelled${field_type}FieldDelegate: NSObjectProtocol {
/// Called when the label is shown or hidden
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelled${field_type}Field, didShowFloatingLabel didShow: Bool)
/// Called when the field becomes or loses first responder status
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelled${field_type}Field, didFocus: Bool)
/// Called when the content of the field changes
@objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelled${field_type}Field)
}
/// An NS${field_type}Field that implements the Float Label Pattern
@IBDesignable open class DSFFloatLabelled${field_type}Field: NS${field_type}Field {
/// Optional delegate to provide callbacks for the floating label state.
///
/// This delegate can be set via Interface Builder or programatically
@IBOutlet @objc public weak var floatLabelDelegate: DSFFloatLabelled${field_type}FieldDelegate?
/// The size (in pt) of the floating label text
@IBInspectable public var placeholderTextSize: CGFloat = NSFont.smallSystemFontSize {
didSet {
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.reconfigureControl()
}
}
/// Spacing between the floating label and the text field text
@IBInspectable public var placeholderSpacing: CGFloat = 0.0 {
didSet {
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.reconfigureControl()
}
}
// Override so that we can notify when the developer changes the text programatically too
open override var stringValue: String {
get {
return super.stringValue
}
set {
super.stringValue = newValue
NotificationCenter.default.post(name: NSControl.textDidChangeNotification, object: self)
}
}
// Override so that we can update when the developer changes the placeholder string
open override var placeholderString: String? {
get {
return super.placeholderString
}
set {
super.placeholderString = newValue
self.floatingLabel.stringValue = newValue ?? ""
}
}
/// Floating label
private let floatingLabel = NSTextField()
/// Is the label currently showing
private var isShowing: Bool = false {
didSet {
self.floatLabelDelegate?.floatLabelledTextField?(self, didShowFloatingLabel: self.isShowing)
}
}
/// Constraint to tie the label to the top of the control
private var floatingTop: NSLayoutConstraint?
/// Height of the control
private var heightConstraint: NSLayoutConstraint?
/// Observers for the font and placeholder text
private var fontObserver: NSKeyValueObservation?
private var placeholderObserver: NSKeyValueObservation?
/// Returns the height of the placeholder text
var placeholderHeight: CGFloat {
let layoutManager = NSLayoutManager()
return layoutManager.defaultLineHeight(for: self.floatingLabel.font!) + 1
}
/// Returns the height of the primary (editable) text
private var textHeight: CGFloat {
let layoutManager = NSLayoutManager()
return layoutManager.defaultLineHeight(for: self.font!) + 1
}
/// Returns the total height of the control given the font settings
private var controlHeight: CGFloat {
return self.textHeight + self.placeholderSpacing + self.placeholderHeight
}
open override var intrinsicContentSize: NSSize {
var sz = super.intrinsicContentSize
sz.height = self.controlHeight
return sz
}
func configureCell() {
let customCell = DSFFloatLabelled${field_type}FieldCell()
customCell.isEditable = true
customCell.wraps = false
customCell.usesSingleLineMode = true
customCell.placeholderString = self.placeholderString
customCell.title = self.stringValue
customCell.font = self.font
customCell.isBordered = self.isBordered
customCell.isBezeled = self.isBezeled
customCell.bezelStyle = self.bezelStyle
customCell.isScrollable = true
customCell.isContinuous = self.isContinuous
customCell.alignment = self.alignment
customCell.formatter = self.formatter
customCell.topOffset = self.placeholderHeight
self.cell = customCell
}
func setTopOffset(_ value: CGFloat) {
guard var f = self.cell as? DSFFloatLabelledTextFieldCellProtocol else {
return
}
f.topOffset = value
}
/// Set the fonts to be used in the control
open func setFonts(primary: NSFont, secondary: NSFont) {
self.floatingLabel.font = secondary
self.font = primary
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.setup()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
}
open override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
// When we're added to the view, make sure that the floating label alignment matches us
// (as the user might have changed the alignment BEFORE adding to the superview
self.floatingLabel.alignment = self.alignment
}
private func setup() {
// Setup the common elements of the control
self.commonSetup()
// Configure a default text cell
self.configureCell()
// Listen to changes in the primary font so we can reconfigure to match
self.fontObserver = self.observe(\.font, options: [.new]) { [weak self] _, _ in
self?.reconfigureControl()
}
// Listen to changes in the placeholder text so we can reflect it in the floater
self.placeholderObserver = self.observe(\.placeholderString, options: [.new]) { [weak self] _, _ in
guard let `self` = self else { return }
self.floatingLabel.stringValue = self.placeholderString!
self.reconfigureControl()
}
}
/// Build the floating label
private func createFloatingLabel() {
if self.floatingLabel.superview == nil {
self.addSubview(self.floatingLabel)
}
self.floatingLabel.wantsLayer = true
self.floatingLabel.isEditable = false
self.floatingLabel.isSelectable = false
self.floatingLabel.isEnabled = true
self.floatingLabel.isBezeled = false
self.floatingLabel.isBordered = false
self.floatingLabel.translatesAutoresizingMaskIntoConstraints = false
self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize)
self.floatingLabel.textColor = NSColor.placeholderTextColor
self.floatingLabel.stringValue = self.placeholderString ?? ""
self.floatingLabel.alphaValue = 0.0
self.floatingLabel.alignment = self.alignment
self.floatingLabel.drawsBackground = false
self.floatingTop = NSLayoutConstraint(
item: self.floatingLabel, attribute: .top,
relatedBy: .equal,
toItem: self, attribute: .top,
multiplier: 1.0, constant: 10
)
self.addConstraint(self.floatingTop!)
var x = NSLayoutConstraint(
item: self.floatingLabel, attribute: .leading,
relatedBy: .equal,
toItem: self, attribute: .leading,
multiplier: 1.0, constant: self.isBezeled ? 4 : 0
)
self.addConstraint(x)
x = NSLayoutConstraint(
item: self.floatingLabel, attribute: .trailing,
relatedBy: .equal,
toItem: self, attribute: .trailing,
multiplier: 1.0, constant: self.isBezeled ? -4 : 0
)
self.addConstraint(x)
self.floatingLabel.setContentHuggingPriority(NSLayoutConstraint.Priority(10), for: .horizontal)
self.floatingLabel.setContentCompressionResistancePriority(NSLayoutConstraint.Priority(10), for: .horizontal)
}
private func commonSetup() {
self.wantsLayer = true
self.translatesAutoresizingMaskIntoConstraints = false
self.usesSingleLineMode = true
self.delegate = self
// Default to natural layout
self.alignment = .natural
self.createFloatingLabel()
self.heightConstraint = NSLayoutConstraint(
item: self, attribute: .height,
relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute,
multiplier: 1.0, constant: self.controlHeight
)
self.addConstraint(self.heightConstraint!)
// If the field already has text, make sure the placeholder is shown
if self.stringValue.count > 0 {
self.showPlaceholder(animated: false)
}
}
/// Change the layout if any changes occur
private func reconfigureControl() {
if self.isCurrentFocus() {
/// If we are currently editing, then finish before changing.
self.window?.endEditing(for: nil)
}
self.expandFrame()
}
/// Rebuild the frame of the text field to match the new settings
private func expandFrame() {
self.heightConstraint?.constant = self.controlHeight
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
open override func layout() {
super.layout()
self.setTopOffset(self.placeholderHeight + self.placeholderSpacing)
self.needsLayout = true
}
}
// MARK: - Focus and editing
extension DSFFloatLabelled${field_type}Field: NSTextFieldDelegate {
// Change the floating label color to represent active state
private func setFloatingLabelActive(_ active: Bool) {
if active {
self.floatingLabel.textColor = NSColor.simulatedAccentColor
}
else {
self.floatingLabel.textColor = NSColor.placeholderTextColor
}
self.floatLabelDelegate?.floatLabelledTextField?(self, didFocus: active)
}
public func controlTextDidChange(_ obj: Notification) {
guard let field = obj.object as? NSTextField else {
return
}
if field.stringValue.count > 0, !self.isShowing {
self.showPlaceholder(animated: true)
}
else if field.stringValue.count == 0, self.isShowing {
self.hidePlaceholder()
}
self.floatLabelDelegate?.floatLabelledTextFieldContentChanged?(self)
}
open override func becomeFirstResponder() -> Bool {
let becomeResult = super.becomeFirstResponder()
if becomeResult, self.isCurrentFocus() {
// Set the color of the 'label' to match the current focus color.
// We need to perform this on the main thread after the current set of notifications are processed
// Why? We (occasionally) receive a 'controlTextDidEndEditing' message AFTER we receive a
// 'becomeFirstResponder'. I've read that this is related to the text field automatically selecting
// text when taking focus, but I haven't been able to verify this in any useful manner.
DispatchQueue.main.async { [weak self] in
if let `self` = self {
self.setFloatingLabelActive(true)
}
}
}
return becomeResult
}
open func controlTextDidEndEditing(_: Notification) {
// When we lose focus, set the label color back to the placeholder color
self.setFloatingLabelActive(false)
}
/// Does our text field currently have input focus?
private func isCurrentFocus() -> Bool {
// 1. Get the window's first responder
// 2. Check is has an active field editor
// 3. Is the delegate of the field editor us?
if let fr = self.window?.firstResponder as? NSTextView,
self.window?.fieldEditor(false, for: nil) != nil,
fr.delegate === self {
return true
}
return false
}
}
// MARK: - Animations
extension DSFFloatLabelled${field_type}Field {
/// Duration of the fade in/out of the secondary label
private var animationDuration: TimeInterval {
return shouldAnimate() ? 0.4 : 0.0
}
/// Returns true if the system is NOT set to reduce motion (accessibility settings)
private func shouldAnimate() -> Bool {
if #available(OSX 10.12, *) {
return !NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
} else {
// Fallback on earlier versions. Just animate
return true
}
}
private func showPlaceholder(animated: Bool) {
self.isShowing = true
if animated {
NSAnimationContext.runAnimationGroup({ [weak self] context in
guard let `self` = self else { return }
context.allowsImplicitAnimation = true
context.duration = self.animationDuration
self.floatingTop?.constant = 0
self.floatingLabel.alphaValue = 1.0
self.layoutSubtreeIfNeeded()
}, completionHandler: {
//
})
}
else {
self.setFloatingLabelActive(false)
self.floatingTop?.constant = 0
self.floatingLabel.alphaValue = 1.0
}
}
private func hidePlaceholder() {
self.isShowing = false
let duration = self.animationDuration
NSAnimationContext.runAnimationGroup({ [weak self] context in
guard let `self` = self else { return }
context.allowsImplicitAnimation = true
context.duration = duration
self.floatingTop?.constant = self.textHeight / 1.5
self.floatingLabel.alphaValue = 0.0
self.layoutSubtreeIfNeeded()
}, completionHandler: {
//
})
}
}
// MARK: - Cell definition
private class DSFFloatLabelled${field_type}FieldCell: NS${field_type}FieldCell, DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat = 0
private func offset() -> CGFloat {
return self.topOffset - (self.isBezeled ? 5 : 1)
}
override func titleRect(forBounds rect: NSRect) -> NSRect {
return NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
}
override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) {
let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
super.edit(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, event: event)
}
override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) {
let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height)
super.select(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength)
}
override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
let insetRect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y + self.offset(), width: cellFrame.width, height: cellFrame.height)
super.drawInterior(withFrame: insetRect, in: controlView)
}
}

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Darren Ford. All rights reserved.</string>
</dict>
</plist>

View File

@ -0,0 +1,46 @@
//
// utils.swift
//
// Copyright © 2023 Darren Ford. All rights reserved.
//
// MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import Foundation
import AppKit
protocol DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat { get set }
}
extension NSColor {
/// A backwards compatible `controlAccentColor`
@inlinable static var simulatedAccentColor: NSColor {
if #available(macOS 10.14, *) {
// macOS 10.14 and above have a dedicated static NSColor
return NSColor.controlAccentColor
}
else {
// Just use the menu highlight color - there's no concept of 'accent' color pre 10.14
return NSColor.selectedMenuItemColor
}
}
}

3
tools/gyb Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
import gyb
gyb.main()

1258
tools/gyb.py Normal file

File diff suppressed because it is too large Load Diff

26
tools/regenerate-sources.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
# A script to re-build the text field sources from the source template file
# Stop on error
set -e
# The path where this script resides
script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
# The path for the package (one level up)
package_path="$( cd -- "${script_path}/../" >/dev/null 2>&1 ; pwd -P )"
pushd .
# We have to run docbuild in the root of the source package
cd "${package_path}"
# Generate the NSTextField implementation
python3 ${script_path}/gyb --line-directive '' -D field_type="Text" -o "DSFFloatLabelledTextField.swift" "Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift.gyb"
# Generate the NSSecureTextField implementation
python3 ${script_path}/gyb --line-directive '' -D field_type="SecureText" -o "DSFFloatLabelledSecureTextField.swift" "Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift.gyb"
popd
echo "Done!"