完善任务流程、键盘适配与页面交互

This commit is contained in:
2026-07-10 15:56:15 +08:00
parent f88a85a807
commit ab5220e460
189 changed files with 16779 additions and 1038 deletions

View File

@ -0,0 +1,14 @@
<?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>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,150 @@
//
// IQBarButtonItem.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers open class IQBarButtonItem: UIBarButtonItem {
internal static let flexibleBarButtonItem: IQBarButtonItem = {
let barButton = IQBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil, action: nil)
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
barButton.hidesSharedBackground = false
}
#endif
return barButton
}()
/**
Fixed space bar button of toolbar.
*/
internal static let fixedSpaceBarButton: IQBarButtonItem = {
let barButton = IQBarButtonItem(barButtonSystemItem: .fixedSpace,
target: nil, action: nil)
barButton.isSystemItem = true
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
barButton.hidesSharedBackground = false
} else {
barButton.width = 6
}
#else
barButton.width = 6
#endif
return barButton
}()
public override init() {
super.init()
initialize()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
let states: [UIControl.State] = [.normal, .highlighted, .disabled, .focused]
for state in states {
setBackgroundImage(UIImage(), for: state, barMetrics: .default)
setBackgroundImage(UIImage(), for: state, style: .plain, barMetrics: .default)
setBackButtonBackgroundImage(UIImage(), for: state, barMetrics: .default)
}
setTitlePositionAdjustment(UIOffset(), for: .default)
setBackgroundVerticalPositionAdjustment(0, for: .default)
setBackButtonBackgroundVerticalPositionAdjustment(0, for: .default)
}
override open var tintColor: UIColor? {
didSet {
var textAttributes: [NSAttributedString.Key: Any] = [:]
textAttributes[.foregroundColor] = tintColor
if let attributes: [NSAttributedString.Key: Any] = titleTextAttributes(for: .normal) {
for (key, value) in attributes {
textAttributes[key] = value
}
}
setTitleTextAttributes(textAttributes, for: .normal)
}
}
/**
Boolean to know if it's a system item or custom item,
we are having a limitation that we cannot override a designated initializer,
so we are manually setting this property once in initialization
*/
internal var isSystemItem: Bool = false
/**
Additional target & action to do get callback action.
Note that setting custom target & selector doesn't affect native functionality,
this is just an additional target to get a callback.
@param target Target object.
@param action Target Selector.
*/
open func setTarget(_ target: AnyObject?, action: Selector?) {
if let target: AnyObject = target, let action: Selector = action {
invocation = IQInvocation(target: target, action: action)
} else {
invocation = nil
}
}
/**
Customized Invocation to be called when button is pressed.
invocation is internally created using setTarget:action: method.
*/
open var invocation: IQInvocation? {
didSet {
// We have to put this condition here because if we override this function then
// We were getting "Cannot override '_' which has been marked unavailable" in Xcode 15
if let titleBarButton = self as? IQTitleBarButtonItem {
if let target = invocation?.target, let action = invocation?.action {
titleBarButton.isEnabled = true
titleBarButton.titleButton.isEnabled = true
titleBarButton.titleButton.addTarget(target, action: action, for: .touchUpInside)
} else {
titleBarButton.isEnabled = false
titleBarButton.titleButton.isEnabled = false
titleBarButton.titleButton.removeTarget(nil, action: nil, for: .touchUpInside)
}
}
}
}
}

View File

@ -0,0 +1,102 @@
//
// IQBarButtonItemConfiguration.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
/**
IQBarButtonItemConfiguration for creating toolbar with bar button items
*/
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers public final class IQBarButtonItemConfiguration: NSObject {
public init(systemItem: UIBarButtonItem.SystemItem, action: Selector? = nil) {
self.systemItem = systemItem
self.image = nil
self.title = nil
self.action = action
super.init()
}
public init(image: UIImage, action: Selector? = nil) {
self.systemItem = nil
self.image = image
self.title = nil
self.action = action
super.init()
}
public init(title: String, action: Selector? = nil) {
self.systemItem = nil
self.image = nil
self.title = title
self.action = action
super.init()
}
public let systemItem: UIBarButtonItem.SystemItem? // System Item to be used to instantiate bar button.
public let image: UIImage? // Image to show on bar button item if it's not a system item.
public let title: String? // Title to show on bar button item if it's not a system item.
public var action: Selector? // action for bar button item. Usually 'doneAction:(IQBarButtonItem*)item'.
public override var accessibilityLabel: String? { didSet { } } // Accessibility related labels
public func apply(on oldBarButtonItem: IQBarButtonItem, target: AnyObject?) -> IQBarButtonItem {
var newBarButtonItem: IQBarButtonItem = oldBarButtonItem
if systemItem == nil, !oldBarButtonItem.isSystemItem {
newBarButtonItem.title = title
newBarButtonItem.image = image
newBarButtonItem.target = target
newBarButtonItem.action = action
} else {
if let systemItem: UIBarButtonItem.SystemItem = systemItem {
newBarButtonItem = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: action)
newBarButtonItem.isSystemItem = true
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
newBarButtonItem.style = .plain
}
#endif
} else if let image: UIImage = image {
newBarButtonItem = IQBarButtonItem(image: image, style: .plain, target: target, action: action)
} else {
newBarButtonItem = IQBarButtonItem(title: title, style: .plain, target: target, action: action)
}
newBarButtonItem.invocation = oldBarButtonItem.invocation
newBarButtonItem.isEnabled = oldBarButtonItem.isEnabled
newBarButtonItem.tag = oldBarButtonItem.tag
}
newBarButtonItem.accessibilityLabel = accessibilityLabel
newBarButtonItem.accessibilityIdentifier = oldBarButtonItem.accessibilityLabel
return newBarButtonItem
}
}

View File

@ -0,0 +1,42 @@
//
// IQInvocation.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers public final class IQInvocation: NSObject {
public weak var target: AnyObject?
public let action: Selector
public init(target: AnyObject, action: Selector) {
self.target = target
self.action = action
}
public func invoke(from: Any) {
guard let target: AnyObject = target else { return }
UIApplication.shared.sendAction(action, to: target, from: from, for: UIEvent())
}
}

View File

@ -0,0 +1,166 @@
//
// IQTitleBarButtonItem.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers open class IQTitleBarButtonItem: IQBarButtonItem {
open var titleFont: UIFont? {
didSet {
if let titleFont: UIFont = titleFont {
titleButton.titleLabel?.font = titleFont
} else {
titleButton.titleLabel?.font = UIFont.systemFont(ofSize: 13)
}
}
}
override open var title: String? {
didSet {
titleButton.setTitle(title, for: .normal)
updateAccessibility()
}
}
/**
titleColor to be used for displaying button text when displaying title (disabled state).
*/
open var titleColor: UIColor? {
didSet {
if let titleColor: UIColor = titleColor {
titleButton.setTitleColor(titleColor, for: .disabled)
} else {
titleButton.setTitleColor(UIColor.lightGray, for: .disabled)
}
}
}
/**
selectableTitleColor to be used for displaying button text when button is enabled.
*/
open var selectableTitleColor: UIColor? {
didSet {
if let selectableTitleColor: UIColor = selectableTitleColor {
titleButton.setTitleColor(selectableTitleColor, for: .normal)
} else {
titleButton.setTitleColor(UIColor.systemBlue, for: .normal)
}
}
}
internal let titleButton: UIButton = UIButton(type: .system)
private let _titleView: UIView = UIView()
private override init() {
super.init()
}
public convenience init(title: String?) {
self.init(title: nil, style: .plain, target: nil, action: nil)
_titleView.backgroundColor = UIColor.clear
titleButton.isAccessibilityElement = false
titleButton.isEnabled = false
titleButton.titleLabel?.numberOfLines = 3
titleButton.setTitleColor(UIColor.lightGray, for: .disabled)
titleButton.setTitleColor(UIColor.systemBlue, for: .normal)
titleButton.backgroundColor = UIColor.clear
titleButton.titleLabel?.textAlignment = .center
titleButton.setTitle(title, for: .normal)
titleFont = UIFont.systemFont(ofSize: 13.0)
titleButton.titleLabel?.font = self.titleFont
_titleView.addSubview(titleButton)
let lowPriority: UILayoutPriority = UILayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue-1)
let highPriority: UILayoutPriority = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue-1)
_titleView.translatesAutoresizingMaskIntoConstraints = false
_titleView.setContentHuggingPriority(lowPriority, for: .vertical)
_titleView.setContentHuggingPriority(lowPriority, for: .horizontal)
_titleView.setContentCompressionResistancePriority(highPriority, for: .vertical)
_titleView.setContentCompressionResistancePriority(highPriority, for: .horizontal)
titleButton.translatesAutoresizingMaskIntoConstraints = false
titleButton.setContentHuggingPriority(lowPriority, for: .vertical)
titleButton.setContentHuggingPriority(lowPriority, for: .horizontal)
titleButton.setContentCompressionResistancePriority(highPriority, for: .vertical)
titleButton.setContentCompressionResistancePriority(highPriority, for: .horizontal)
let top: NSLayoutConstraint = NSLayoutConstraint(item: titleButton, attribute: .top,
relatedBy: .equal,
toItem: _titleView, attribute: .top,
multiplier: 1, constant: 0)
let bottom: NSLayoutConstraint = NSLayoutConstraint(item: titleButton, attribute: .bottom,
relatedBy: .equal,
toItem: _titleView, attribute: .bottom,
multiplier: 1, constant: 0)
let leading: NSLayoutConstraint = NSLayoutConstraint(item: titleButton, attribute: .leading,
relatedBy: .equal,
toItem: _titleView, attribute: .leading,
multiplier: 1, constant: 0)
let trailing: NSLayoutConstraint = NSLayoutConstraint(item: titleButton, attribute: .trailing,
relatedBy: .equal,
toItem: _titleView, attribute: .trailing,
multiplier: 1, constant: 0)
_titleView.addConstraints([top, bottom, leading, trailing])
customView = _titleView
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func updateAccessibility() {
if title == nil || title?.isEmpty == true {
isAccessibilityElement = false
// Swift 6: Reference to static property 'none'
// is not concurrency-safe because it involves shared mutable state
// accessibilityTraits = .none
accessibilityTraits = .init(rawValue: 0)
} else if titleButton.isEnabled == true {
isAccessibilityElement = true
// Swift 6: Reference to static property 'button'
// is not concurrency-safe because it involves shared mutable state
// accessibilityTraits = .button
accessibilityTraits = .init(rawValue: 1)
} else {
isAccessibilityElement = true
// Swift 6: Reference to static property 'staticText'
// is not concurrency-safe because it involves shared mutable state
// accessibilityTraits = .staticText
accessibilityTraits = .init(rawValue: 64)
}
}
}

View File

@ -0,0 +1,389 @@
//
// UIView+IQKeyboardExtension.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
import IQKeyboardCore
@available(iOSApplicationExtension, unavailable)
@MainActor
private struct AssociatedKeys {
static var toolbar: Int = 0
static var hidePlaceholder: Int = 0
static var placeholder: Int = 0
}
@available(iOSApplicationExtension, unavailable)
@MainActor
public extension IQKeyboardExtension where Base: IQTextInputView {
// MARK: Toolbar
/**
Toolbar references for better customization control.
*/
var toolbar: IQKeyboardToolbar {
var toolbar: IQKeyboardToolbar? = base?.inputAccessoryView as? IQKeyboardToolbar
if toolbar == nil, let base = base {
toolbar = objc_getAssociatedObject(base, &AssociatedKeys.toolbar) as? IQKeyboardToolbar
}
if let toolbar: IQKeyboardToolbar = toolbar {
return toolbar
} else {
let width: CGFloat = base?.window?.windowScene?.screen.bounds.width ?? 0
let height: CGFloat
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
height = 58
} else {
height = 44
}
#else
height = 44
#endif
let frame = CGRect(origin: .zero, size: .init(width: width, height: height))
let newToolbar = IQKeyboardToolbar(frame: frame)
if let base = base {
objc_setAssociatedObject(base, &AssociatedKeys.toolbar, newToolbar, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
return newToolbar
}
}
// MARK: Toolbar title
/**
If `hideToolbarPlaceholder` is YES, then title will not be added to the toolbar. Default to NO.
*/
var hidePlaceholder: Bool {
get {
if let base = base {
return objc_getAssociatedObject(base, &AssociatedKeys.hidePlaceholder) as? Bool ?? false
}
return false
}
set(newValue) {
if let base = base {
objc_setAssociatedObject(base, &AssociatedKeys.hidePlaceholder,
newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
toolbar.titleBarButton.title = drawingPlaceholder
}
}
}
/**
`toolbarPlaceholder` to override default `placeholder` text when drawing text on toolbar.
*/
var placeholder: String? {
get {
if let base = base {
return objc_getAssociatedObject(base, &AssociatedKeys.placeholder) as? String
}
return nil
}
set(newValue) {
if let base = base {
// swiftlint:disable line_length
objc_setAssociatedObject(base, &AssociatedKeys.placeholder, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
toolbar.titleBarButton.title = drawingPlaceholder
// swiftlint:enable line_length
}
}
}
/**
`drawingToolbarPlaceholder` will be actual text used to draw on toolbar.
This would either `placeholder` or `toolbarPlaceholder`.
*/
var drawingPlaceholder: String? {
guard !hidePlaceholder else { return nil }
if let placeholder = placeholder,
!placeholder.isEmpty {
return placeholder
}
guard let placeholderable: any IQPlaceholderable = base as? (any IQPlaceholderable) else { return nil }
if let placeholder = placeholderable.attributedPlaceholder?.string,
!placeholder.isEmpty {
return placeholder
} else if let placeholder = placeholderable.placeholder {
return placeholder
} else {
return nil
}
}
// MARK: Common
func addToolbar(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration? = nil,
title: String?,
titleAccessibilityLabel: String? = nil) {
guard let base = base else { return }
// Creating a toolBar for phoneNumber keyboard
let toolbar: IQKeyboardToolbar = toolbar
let items: [UIBarButtonItem] = Self.constructBarButtonItems(target: target, toolbar: toolbar,
previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
// Adding button to toolBar.
toolbar.items = items
switch base.keyboardAppearance {
case .dark:
toolbar.barStyle = .black
default:
toolbar.barStyle = .default
}
// Setting toolbar to keyboard.
let reloadInputViews: Bool = base.inputAccessoryView != toolbar
guard reloadInputViews else { return }
base.inputAccessoryView = toolbar
base.reloadInputViews()
}
// MARK: Right
func addDone(target: AnyObject?,
action: Selector,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
let title: String? = showPlaceholder ? drawingPlaceholder : nil
addDone(target: target, action: action,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addDone(target: AnyObject?,
action: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
let rightConfiguration: IQBarButtonItemConfiguration
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
rightConfiguration = IQBarButtonItemConfiguration(image: UIImage(systemName: "checkmark")!, action: action)
} else {
rightConfiguration = IQBarButtonItemConfiguration(systemItem: .done, action: action)
}
#else
rightConfiguration = IQBarButtonItemConfiguration(systemItem: .done, action: action)
#endif
addToolbar(target: target, rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
let title: String? = showPlaceholder ? drawingPlaceholder : nil
addRightButton(target: target, configuration: configuration, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
addToolbar(target: target, rightConfiguration: configuration, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
// MARK: Right/Left
func addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration, leftConfiguration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
let title: String? = showPlaceholder ? drawingPlaceholder : nil
addRightLeft(target: target,
rightConfiguration: rightConfiguration, leftConfiguration: leftConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration, leftConfiguration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
addToolbar(target: target,
previousConfiguration: leftConfiguration, rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
// MARK: Previous/Next/Right
func addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
let title: String? = showPlaceholder ? drawingPlaceholder : nil
addPreviousNextRight(target: target,
previousConfiguration: previousConfiguration, nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
title: String?, titleAccessibilityLabel: String? = nil) {
addToolbar(target: target,
previousConfiguration: previousConfiguration, nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addPreviousNextDone(target: AnyObject?, previousAction: Selector, nextAction: Selector, doneAction: Selector,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
let title: String? = showPlaceholder ? drawingPlaceholder : nil
addPreviousNextDone(target: target, previousAction: previousAction, nextAction: nextAction,
doneAction: doneAction,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func addPreviousNextDone(target: AnyObject?,
previousAction: Selector, nextAction: Selector, doneAction: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
let chevronUp: UIImage = UIImage(systemName: "chevron.up") ?? UIImage()
let chevronDown: UIImage = UIImage(systemName: "chevron.down") ?? UIImage()
let previousConfiguration = IQBarButtonItemConfiguration(image: chevronUp,
action: previousAction)
let nextConfiguration = IQBarButtonItemConfiguration(image: chevronDown,
action: nextAction)
let rightConfiguration: IQBarButtonItemConfiguration
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
rightConfiguration = IQBarButtonItemConfiguration(image: UIImage(systemName: "checkmark")!, action: doneAction)
} else {
rightConfiguration = IQBarButtonItemConfiguration(systemItem: .done, action: doneAction)
}
#else
rightConfiguration = IQBarButtonItemConfiguration(systemItem: .done, action: doneAction)
#endif
addToolbar(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration, rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
}
@available(iOSApplicationExtension, unavailable)
@MainActor
private extension IQKeyboardExtension where Base: IQTextInputView {
private static func constructBarButtonItems(target: AnyObject?,
toolbar: IQKeyboardToolbar,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration? = nil,
title: String?,
titleAccessibilityLabel: String? = nil) -> [UIBarButtonItem] {
var items: [UIBarButtonItem] = []
// Leading group
do {
if let previousConfiguration: IQBarButtonItemConfiguration = previousConfiguration {
let prev: IQBarButtonItem = previousConfiguration.apply(on: toolbar.previousBarButton, target: target)
toolbar.previousBarButton = prev
items.append(prev)
}
if previousConfiguration != nil, nextConfiguration != nil {
items.append(IQBarButtonItem.fixedSpaceBarButton)
}
if let nextConfiguration: IQBarButtonItemConfiguration = nextConfiguration {
let next: IQBarButtonItem = nextConfiguration.apply(on: toolbar.nextBarButton, target: target)
toolbar.nextBarButton = next
items.append(next)
}
if !toolbar.additionalLeadingItems.isEmpty {
items.append(contentsOf: toolbar.additionalLeadingItems)
}
}
// Center Title bar button item
if let title = title, !title.isEmpty {
// Title button
toolbar.titleBarButton.title = title
toolbar.titleBarButton.accessibilityLabel = titleAccessibilityLabel
toolbar.titleBarButton.accessibilityIdentifier = titleAccessibilityLabel
toolbar.titleBarButton.customView?.frame = .zero
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
if !items.isEmpty {
items.append(IQBarButtonItem.flexibleBarButtonItem)
}
} else {
items.append(IQBarButtonItem.flexibleBarButtonItem)
}
#else
items.append(IQBarButtonItem.flexibleBarButtonItem)
#endif
items.append(toolbar.titleBarButton)
}
// Flexible space
items.append(IQBarButtonItem.flexibleBarButtonItem)
// Trailing group
do {
if !toolbar.additionalTrailingItems.isEmpty {
items.append(contentsOf: toolbar.additionalTrailingItems)
}
if let rightConfiguration: IQBarButtonItemConfiguration = rightConfiguration {
let done: IQBarButtonItem = rightConfiguration.apply(on: toolbar.doneBarButton, target: target)
toolbar.doneBarButton = done
items.append(done)
}
}
return items
}
}

View File

@ -0,0 +1,228 @@
//
// UIView+IQKeyboardExtensionDeprecated.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
// swiftlint:disable unused_setter_value
// swiftlint:disable line_length
// swiftlint:disable function_parameter_count
@available(iOSApplicationExtension, unavailable)
@MainActor
@objc public extension UIView {
@available(*, unavailable, renamed: "iq.toolbar")
var keyboardToolbar: IQKeyboardToolbar {
get { fatalError() }
set {}
}
@available(*, unavailable, renamed: "iq.hidePlaceholder")
var shouldHideToolbarPlaceholder: Bool {
get { false }
set {}
}
@available(*, unavailable, renamed: "iq.placeholder")
var toolbarPlaceholder: String? {
get { nil }
set {}
}
@available(*, unavailable, renamed: "iq.drawingPlaceholder")
var drawingToolbarPlaceholder: String? {
get { nil }
set {}
}
@available(*, unavailable, renamed: "iq.addToolbar(target:previousConfiguration:nextConfiguration:rightConfiguration:title:titleAccessibilityLabel:)")
func addKeyboardToolbarWithTarget(target: AnyObject?,
titleText: String?,
titleAccessibilityLabel: String? = nil,
rightBarButtonConfiguration: IQBarButtonItemConfiguration?,
previousBarButtonConfiguration: IQBarButtonItemConfiguration? = nil,
nextBarButtonConfiguration: IQBarButtonItemConfiguration? = nil) {
}
@available(*, unavailable, renamed: "iq.addDone(target:action:showPlaceholder:titleAccessibilityLabel:)")
func addDoneOnKeyboardWithTarget(_ target: AnyObject?,
action: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addDone(target:action:title:titleAccessibilityLabel:)")
func addDoneOnKeyboardWithTarget(_ target: AnyObject?,
action: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightButton(target:configuration:showPlaceholder:titleAccessibilityLabel:)")
func addRightButtonOnKeyboardWithImage(_ image: UIImage,
target: AnyObject?,
action: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightButton(target:configuration:title:titleAccessibilityLabel:)")
func addRightButtonOnKeyboardWithImage(_ image: UIImage,
target: AnyObject?,
action: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightButton(target:configuration:showPlaceholder:titleAccessibilityLabel:)")
func addRightButtonOnKeyboardWithText(_ text: String,
target: AnyObject?,
action: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightButton(target:configuration:title:titleAccessibilityLabel:)")
func addRightButtonOnKeyboardWithText(_ text: String,
target: AnyObject?,
action: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:showPlaceholder:titleAccessibilityLabel:)")
func addCancelDoneOnKeyboardWithTarget(_ target: AnyObject?,
cancelAction: Selector,
doneAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:showPlaceholder:titleAccessibilityLabel:)")
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?,
leftButtonTitle: String,
rightButtonTitle: String,
leftButtonAction: Selector,
rightButtonAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:showPlaceholder:titleAccessibilityLabel:)")
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?,
leftButtonImage: UIImage,
rightButtonImage: UIImage,
leftButtonAction: Selector,
rightButtonAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:title:titleAccessibilityLabel:)")
func addCancelDoneOnKeyboardWithTarget(_ target: AnyObject?,
cancelAction: Selector,
doneAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:title:titleAccessibilityLabel:)")
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?,
leftButtonTitle: String,
rightButtonTitle: String,
leftButtonAction: Selector,
rightButtonAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addRightLeft(target:rightConfiguration:leftConfiguration:title:titleAccessibilityLabel:)")
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?,
leftButtonImage: UIImage,
rightButtonImage: UIImage,
leftButtonAction: Selector,
rightButtonAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextDone(target:previousAction:nextAction:doneAction:showPlaceholder:titleAccessibilityLabel:)")
func addPreviousNextDone(_ target: AnyObject?,
previousAction: Selector,
nextAction: Selector,
doneAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextDone(target:previousAction:nextAction:doneAction:title:titleAccessibilityLabel:)")
func addPreviousNextDoneOnKeyboardWithTarget(_ target: AnyObject?,
previousAction: Selector,
nextAction: Selector,
doneAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextRight(target:previousConfiguration:nextConfiguration:rightConfiguration:showPlaceholder:titleAccessibilityLabel:)")
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?,
rightButtonImage: UIImage,
previousAction: Selector,
nextAction: Selector,
rightButtonAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextRight(target:previousConfiguration:nextConfiguration:rightConfiguration:showPlaceholder:titleAccessibilityLabel:)")
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?,
rightButtonTitle: String,
previousAction: Selector,
nextAction: Selector,
rightButtonAction: Selector,
shouldShowPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextRight(target:previousConfiguration:nextConfiguration:rightConfiguration:title:titleAccessibilityLabel:)")
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?,
rightButtonImage: UIImage,
previousAction: Selector,
nextAction: Selector,
rightButtonAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
@available(*, unavailable, renamed: "iq.addPreviousNextRight(target:previousConfiguration:nextConfiguration:rightConfiguration:title:titleAccessibilityLabel:)")
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?,
rightButtonTitle: String,
previousAction: Selector,
nextAction: Selector,
rightButtonAction: Selector,
titleText: String?,
titleAccessibilityLabel: String? = nil) {
}
}
// swiftlint:enable unused_setter_value
// swiftlint:enable line_length
// swiftlint:enable function_parameter_count

View File

@ -0,0 +1,254 @@
//
// UIView+IQKeyboardExtensionObjc.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
// MARK: For ObjectiveC Compatibility
// swiftlint:disable identifier_name
@objc public extension UITextField {
var iq_toolbar: IQKeyboardToolbar { iq.toolbar }
var iq_hidePlaceholder: Bool {
get { iq.hidePlaceholder }
set(newValue) { iq.hidePlaceholder = newValue }
}
var iq_placeholder: String? {
get { iq.placeholder }
set(newValue) { iq.placeholder = newValue }
}
var iq_drawingPlaceholder: String? { iq.drawingPlaceholder }
func iq_addToolbar(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration? = nil,
title: String?,
titleAccessibilityLabel: String? = nil) {
iq.addToolbar(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addDone(target: AnyObject?,
action: Selector,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addDone(target: target, action: action, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addDone(target: AnyObject?,
action: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addDone(target: target, action: action, title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addRightButton(target: target, configuration: configuration, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addRightButton(target: target, configuration: configuration, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration,
leftConfiguration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addRightLeft(target: target, rightConfiguration: rightConfiguration,
leftConfiguration: leftConfiguration, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration,
leftConfiguration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addRightLeft(target: target, rightConfiguration: rightConfiguration,
leftConfiguration: leftConfiguration, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextRight(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration, rightConfiguration: rightConfiguration,
showPlaceholder: showPlaceholder, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextRight(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextDone(target: AnyObject?, previousAction: Selector,
nextAction: Selector, doneAction: Selector,
showPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextDone(target: target, previousAction: previousAction,
nextAction: nextAction, doneAction: doneAction,
showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextDone(target: AnyObject?,
previousAction: Selector, nextAction: Selector, doneAction: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextDone(target: target, previousAction: previousAction,
nextAction: nextAction, doneAction: doneAction,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
}
@objc public extension UITextView {
var iq_toolbar: IQKeyboardToolbar { iq.toolbar }
var iq_hidePlaceholder: Bool {
get { iq.hidePlaceholder }
set(newValue) { iq.hidePlaceholder = newValue }
}
var iq_placeholder: String? {
get { iq.placeholder }
set(newValue) { iq.placeholder = newValue }
}
var iq_drawingPlaceholder: String? { iq.drawingPlaceholder }
func iq_addToolbar(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration? = nil,
title: String?,
titleAccessibilityLabel: String? = nil) {
iq.addToolbar(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration, rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addDone(target: AnyObject?,
action: Selector,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addDone(target: target, action: action, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addDone(target: AnyObject?,
action: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addDone(target: target, action: action, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addRightButton(target: target, configuration: configuration,
showPlaceholder: showPlaceholder, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightButton(target: AnyObject?,
configuration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addRightButton(target: target, configuration: configuration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration,
leftConfiguration: IQBarButtonItemConfiguration,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addRightLeft(target: target, rightConfiguration: rightConfiguration,
leftConfiguration: leftConfiguration, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addRightLeft(target: AnyObject?,
rightConfiguration: IQBarButtonItemConfiguration,
leftConfiguration: IQBarButtonItemConfiguration,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addRightLeft(target: target, rightConfiguration: rightConfiguration,
leftConfiguration: leftConfiguration, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
showPlaceholder: Bool = false, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextRight(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration, rightConfiguration: rightConfiguration,
showPlaceholder: showPlaceholder, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextRight(target: AnyObject?,
previousConfiguration: IQBarButtonItemConfiguration? = nil,
nextConfiguration: IQBarButtonItemConfiguration? = nil,
rightConfiguration: IQBarButtonItemConfiguration?,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextRight(target: target, previousConfiguration: previousConfiguration,
nextConfiguration: nextConfiguration,
rightConfiguration: rightConfiguration,
title: title, titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextDone(target: AnyObject?, previousAction: Selector, nextAction: Selector,
doneAction: Selector, showPlaceholder: Bool = false,
titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextDone(target: target, previousAction: previousAction,
nextAction: nextAction,
doneAction: doneAction, showPlaceholder: showPlaceholder,
titleAccessibilityLabel: titleAccessibilityLabel)
}
func iq_addPreviousNextDone(target: AnyObject?,
previousAction: Selector, nextAction: Selector, doneAction: Selector,
title: String?, titleAccessibilityLabel: String? = nil) {
iq.addPreviousNextDone(target: target, previousAction: previousAction,
nextAction: nextAction,
doneAction: doneAction, title: title,
titleAccessibilityLabel: titleAccessibilityLabel)
}
}
// swiftlint:enable identifier_name

View File

@ -0,0 +1,183 @@
//
// IQKeyboardToolbar.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
/** @abstract IQKeyboardToolbar. */
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers open class IQKeyboardToolbar: UIToolbar {
override public init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
sizeToFit()
autoresizingMask = .flexibleWidth
self.isTranslucent = true
self.barTintColor = nil
let positions: [UIBarPosition] = [.any, .bottom, .top, .topAttached]
for position in positions {
self.setBackgroundImage(nil, forToolbarPosition: position, barMetrics: .default)
self.setShadowImage(nil, forToolbarPosition: .any)
}
// Background color
self.backgroundColor = nil
}
/**
Additional bar buttons to show at the leading side.
*/
open var additionalLeadingItems: [UIBarButtonItem] = []
/**
Additional bar buttons to show at the trailing side.
*/
open var additionalTrailingItems: [UIBarButtonItem] = []
/**
Previous bar button of toolbar.
*/
private var privatePreviousBarButton: IQBarButtonItem?
open var previousBarButton: IQBarButtonItem {
get {
if privatePreviousBarButton == nil {
privatePreviousBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
}
return privatePreviousBarButton!
}
set (newValue) {
privatePreviousBarButton = newValue
}
}
/**
Next bar button of toolbar.
*/
private var privateNextBarButton: IQBarButtonItem?
open var nextBarButton: IQBarButtonItem {
get {
if privateNextBarButton == nil {
privateNextBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
}
return privateNextBarButton!
}
set (newValue) {
privateNextBarButton = newValue
}
}
/**
Title bar button of toolbar.
*/
private var privateTitleBarButton: IQTitleBarButtonItem?
open var titleBarButton: IQTitleBarButtonItem {
get {
if privateTitleBarButton == nil {
privateTitleBarButton = IQTitleBarButtonItem(title: nil)
}
return privateTitleBarButton!
}
set (newValue) {
privateTitleBarButton = newValue
}
}
/**
Done bar button of toolbar.
*/
private var privateDoneBarButton: IQBarButtonItem?
open var doneBarButton: IQBarButtonItem {
get {
if privateDoneBarButton == nil {
privateDoneBarButton = IQBarButtonItem(title: nil, style: .done, target: nil, action: nil)
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
privateDoneBarButton?.style = .plain
}
#endif
}
return privateDoneBarButton!
}
set (newValue) {
privateDoneBarButton = newValue
}
}
override open func sizeThatFits(_ size: CGSize) -> CGSize {
var sizeThatFit: CGSize = super.sizeThatFits(size)
let height: CGFloat
#if compiler(>=6.2) // Xcode 26
let requiresCompatibility: Bool = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
if #available(iOS 26.0, *), !requiresCompatibility {
height = 58
} else {
height = 44
}
#else
height = 44
#endif
sizeThatFit.height = height
return sizeThatFit
}
override open var tintColor: UIColor! {
didSet {
guard let items: [UIBarButtonItem] = items else { return }
for item in items {
item.tintColor = tintColor
}
}
}
}
@available(iOSApplicationExtension, unavailable)
@MainActor
@objc extension IQKeyboardToolbar: UIInputViewAudioFeedback {
open var enableInputClicksWhenVisible: Bool {
return true
}
}

View File

@ -0,0 +1,60 @@
//
// IQKeyboardToolbarPlaceholderConfiguration.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
// swiftlint:disable type_name
@available(iOSApplicationExtension, unavailable)
@MainActor
@objcMembers public final class IQKeyboardToolbarPlaceholderConfiguration: NSObject {
/**
If YES, then it add the textInputView's placeholder text on toolbar. Default is YES.
*/
public var showPlaceholder: Bool = true
/**
Placeholder Font. Default is nil.
*/
public var font: UIFont?
/**
Placeholder Color. Default is nil. Which means lightGray
*/
public var color: UIColor?
/**
Placeholder Button Color when it's treated as button. Default is nil.
*/
public var buttonColor: UIColor?
/**
Placeholder accessibility Label
*/
public override var accessibilityLabel: String? { didSet { } }
}
// swiftlint:enable type_name
@available(*, unavailable, renamed: "IQKeyboardToolbarPlaceholderConfiguration")
@MainActor
@objcMembers public final class IQToolbarPlaceholderConfiguration: NSObject {}

View File

@ -0,0 +1,37 @@
//
// IQPlaceholderable.swift
// https://github.com/hackiftekhar/IQKeyboardToolbar
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// 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 UIKit
@available(iOSApplicationExtension, unavailable)
@MainActor
@objc public protocol IQPlaceholderable: AnyObject {
var placeholder: String? { get set }
var attributedPlaceholder: NSAttributedString? { get set }
}
@available(iOSApplicationExtension, unavailable)
@MainActor
@objc extension UITextField: IQPlaceholderable { }

21
Pods/IQKeyboardToolbar/LICENSE generated Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Mohd Iftekhar Qurashi
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.

85
Pods/IQKeyboardToolbar/README.md generated Normal file
View File

@ -0,0 +1,85 @@
# IQKeyboardToolbar
[![CI Status](https://img.shields.io/travis/hackiftekhar/IQKeyboardToolbar.svg?style=flat)](https://travis-ci.org/hackiftekhar/IQKeyboardToolbar)
[![Version](https://img.shields.io/cocoapods/v/IQKeyboardToolbar.svg?style=flat)](https://cocoapods.org/pods/IQKeyboardToolbar)
[![License](https://img.shields.io/cocoapods/l/IQKeyboardToolbar.svg?style=flat)](https://cocoapods.org/pods/IQKeyboardToolbar)
[![Platform](https://img.shields.io/cocoapods/p/IQKeyboardToolbar.svg?style=flat)](https://cocoapods.org/pods/IQKeyboardToolbar)
![Screenshot1](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardToolbar/master/Screenshot/IQKeyboardToolbarScreenshot.png)
![Screenshot2](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardToolbar/master/Screenshot/IQKeyboardToolbarScreenshot2.png)
![Screenshot3](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardToolbar/master/Screenshot/IQKeyboardToolbarScreenshot3.png)
![Screenshot4](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardToolbar/master/Screenshot/IQKeyboardToolbarScreenshot4.png)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
IQKeyboardToolbar is available through [CocoaPods](https://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'IQKeyboardToolbar'
```
## Usage
This library can be used to add toolbar to the keyboard
Previous Next and Done button
```swift
textField.iq.addPreviousNextDone(target: self,
previousAction: #selector(textField1PreviousAction),
nextAction: #selector(textField1NextAction),
doneAction: #selector(doneAction), showPlaceholder: true)
```
Previous Next and Right button with customized titles or images
```swift
textField.iq.addPreviousNextRight(target: self,
previousConfiguration: .init(title: "Prev", action: #selector(textView1PreviousAction)),
nextConfiguration: .init(title: "Next", action: #selector(textView1NextAction)),
rightConfiguration: .init(image: UIImage(systemName: "chevron.down")!, action: #selector(doneAction)),
title: "Text View 1")
```
Action button
```swift
textField.iq.addDone(target: self,
action: #selector(doneAction),
title: "Select Account")
textField.iq.toolbar.titleBarButton.setTarget(self,
action: #selector(selectAccount))
```
Additional leading and trailing buttons
```swift
textField.iq.toolbar.additionalLeadingItems = [.init(barButtonSystemItem: .add, target: self, action: #selector(addAction))]
textField.iq.toolbar.additionalTrailingItems = [.init(barButtonSystemItem: .camera, target: self, action: #selector(cameraAction))]
textField.iq.addToolbar(target: self,
previousConfiguration: nil,
nextConfiguration: .init(title: "Next", action: #selector(doneAction)),
rightConfiguration: .init(title: "Finish", action: #selector(doneAction)),
title: "TextView 2")
```
Hide Placeholder
```swift
textField1.iq.hidePlaceholder = true
```
Customized Placeholder
```swift
textField1.iq.placeholder = "My Own Placeholder"
```
## Author
Iftekhar Qurashi hack.iftekhar@gmail.com
## License
IQKeyboardToolbar is available under the MIT license. See the LICENSE file for more info.