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

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 { }