gpt4 book ai didi

ios - 如何在swift项目中使用TPKeyboardAvoiding?

转载 作者:搜寻专家 更新时间:2023-10-31 08:16:32 25 4
gpt4 key购买 nike

我在我的 Objective c 应用程序中使用了 TPKeyboardAvoiding scrollview。现在我想在我的 swift 项目中使用它。我进行了搜索,但没有找到具体的步骤。

感谢帮助

最佳答案

Swift 4.1,只需将类相应地分配给 Storyboard或 xib 中的 ScrollView 即可。

// MARK: - TableView
class TPKeyboardAvoidingTableView:UITableView,UITextFieldDelegate, UITextViewDelegate {

override var frame:CGRect{
willSet{
super.frame = frame
}

didSet{
if hasAutomaticKeyboardAvoidingBehaviour() {return}
TPKeyboardAvoiding_updateContentInset()
}
}

override var contentSize:CGSize{
willSet(newValue){
if hasAutomaticKeyboardAvoidingBehaviour() {
super.contentSize = newValue
return
}

if newValue.equalTo(self.contentSize)
{
return
}

super.contentSize = newValue
self.TPKeyboardAvoiding_updateContentInset()
}

// didSet{
// self.TPKeyboardAvoiding_updateContentInset()
// }
}


override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
self.setup()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}

override func awakeFromNib() {
setup()
}

deinit{
NotificationCenter.default.removeObserver(self)
}

func hasAutomaticKeyboardAvoidingBehaviour()->Bool
{
if #available(iOS 8.3, *) {
if self.delegate is UITableViewController
{
return true
}
}

return false
}

func focusNextTextField()->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}

@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}

override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}

override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}

private extension TPKeyboardAvoidingTableView
{
func setup()
{
if self.hasAutomaticKeyboardAvoidingBehaviour() { return }

NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}

// MARK: - CollectionView
class TPKeyboardAvoidingCollectionView:UICollectionView,UITextViewDelegate {

override var contentSize:CGSize{
willSet(newValue){
if newValue.equalTo(self.contentSize)
{
return
}

super.contentSize = newValue
self.TPKeyboardAvoiding_updateContentInset()
}

}


override var frame:CGRect{
willSet{
super.frame = frame
}

didSet{
self.TPKeyboardAvoiding_updateContentInset()
}
}

// override init(frame: CGRect) {
// super.init(frame: frame)
// }

override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
setup()
}

required init?(coder aDecoder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
self.setup()

}

override func awakeFromNib() {
setup()
}

deinit{
NotificationCenter.default.removeObserver(self)
}

func focusNextTextField()->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}

@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}

override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}

override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}

private extension TPKeyboardAvoidingCollectionView
{
func setup()
{
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}

// MARK: - ScrollView
class TPKeyboardAvoidingScrollView:UIScrollView,UITextFieldDelegate,UITextViewDelegate
{
override var contentSize:CGSize{
didSet{
self.TPKeyboardAvoiding_updateFromContentSizeChange()
}
}


override var frame:CGRect{
didSet{
self.TPKeyboardAvoiding_updateContentInset()
}
}

override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}

override func awakeFromNib() {
setup()
}

func contentSizeToFit()
{
self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
}

func focusNextTextField() ->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}

@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}

deinit{
NotificationCenter.default.removeObserver(self)
}

override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}

override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}

private extension TPKeyboardAvoidingScrollView
{
func setup()
{
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}

// MARK: - Process Event
let kCalculatedContentPadding:CGFloat = 10;
let kMinimumScrollOffsetPadding:CGFloat = 20;

extension UIScrollView
{
@objc func TPKeyboardAvoiding_keyboardWillShow(_ notification:Notification)
{
guard let userInfo = notification.userInfo else { return }
guard let rectNotification = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else
{
return
}

let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
if keyboardRect.isEmpty
{
return
}

let state = self.keyboardAvoidingState()

guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return}
state.keyboardRect = keyboardRect
if !state.keyboardVisible
{
state.priorInset = self.contentInset
state.priorScrollIndicatorInsets = self.scrollIndicatorInsets
state.priorPagingEnabled = self.isPagingEnabled
}

state.keyboardVisible = true
self.isPagingEnabled = false

if self is TPKeyboardAvoidingScrollView
{
state.priorContentSize = self.contentSize
if self.contentSize.equalTo(CGSize.zero)
{
self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
}
}

let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
let options = UIViewAnimationOptions(rawValue: UInt(curve))

UIView.animate(withDuration: TimeInterval(duration),
delay: 0,
options: options,
animations: { [weak self]() -> Void in
if let actualSelf = self
{
actualSelf.contentInset = actualSelf.TPKeyboardAvoiding_contentInsetForKeyboard()
let viewableHeight = actualSelf.bounds.size.height - actualSelf.contentInset.top - actualSelf.contentInset.bottom
let point = CGPoint(x: actualSelf.contentOffset.x, y: actualSelf.TPKeyboardAvoiding_idealOffsetForView(firstResponder, viewAreaHeight: viewableHeight))
actualSelf.setContentOffset(point, animated: false)

actualSelf.scrollIndicatorInsets = actualSelf.contentInset
actualSelf.layoutIfNeeded()
}

}) { (finished) -> Void in

}
}

@objc func TPKeyboardAvoiding_keyboardWillHide(_ notification:Notification)
{
guard let userInfo = notification.userInfo else { return }

guard let rectNotification = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else
{
return
}
let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
if keyboardRect.isEmpty
{
return
}
let state = self.keyboardAvoidingState()

if !state.keyboardVisible
{
return
}
state.keyboardRect = CGRect.zero
state.keyboardVisible = false

let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
let options = UIViewAnimationOptions(rawValue: UInt(curve))

UIView.animate(withDuration: TimeInterval(duration),
delay: 0,
options: options,
animations: { [weak self]() -> Void in
if let actualSelf = self
{
if actualSelf is TPKeyboardAvoidingScrollView {
actualSelf.contentSize = state.priorContentSize
actualSelf.contentInset = state.priorInset
actualSelf.scrollIndicatorInsets = state.priorScrollIndicatorInsets
actualSelf.isPagingEnabled = state.priorPagingEnabled
actualSelf.layoutIfNeeded()
}
}

}) { (finished) -> Void in

}
}

func TPKeyboardAvoiding_updateFromContentSizeChange()
{
let state = self.keyboardAvoidingState()
if state.keyboardVisible
{
state.priorContentSize = self.contentSize
}
}

func TPKeyboardAvoiding_focusNextTextField() ->Bool
{
guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return false}
guard let view = self.TPKeyboardAvoiding_findNextInputViewAfterView(firstResponder, beneathView: self) else { return false}
Timer.scheduledTimer(timeInterval: 0.1, target: view, selector: #selector(becomeFirstResponder), userInfo: nil, repeats: false)

return true

}

func TPKeyboardAvoiding_scrollToActiveTextField()
{
let state = self.keyboardAvoidingState()

if !state.keyboardVisible { return }

let visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom

let idealOffset = CGPoint(x: 0,
y: self.TPKeyboardAvoiding_idealOffsetForView(self.TPKeyboardAvoiding_findFirstResponderBeneathView(self),
viewAreaHeight: visibleSpace))

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double((Int64)(0 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {[weak self] () -> Void in
self?.setContentOffset(idealOffset, animated: true)
}
}

//Helper
func TPKeyboardAvoiding_findFirstResponderBeneathView(_ view:UIView) -> UIView?
{
for childView in view.subviews
{
if childView.responds(to: #selector(getter: isFirstResponder)) && childView.isFirstResponder
{
return childView
}
let result = TPKeyboardAvoiding_findFirstResponderBeneathView(childView)
if result != nil
{
return result
}
}
return nil
}

func TPKeyboardAvoiding_updateContentInset()
{
let state = self.keyboardAvoidingState()
if state.keyboardVisible
{
self.contentInset = self.TPKeyboardAvoiding_contentInsetForKeyboard()
}
}

func TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames() ->CGSize
{
let wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator
let wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator

self.showsVerticalScrollIndicator = false
self.showsHorizontalScrollIndicator = false

var rect = CGRect.zero

for view in self.subviews
{
rect = rect.union(view.frame)
}

rect.size.height += kCalculatedContentPadding
self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator
self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator

return rect.size
}

func TPKeyboardAvoiding_idealOffsetForView(_ view:UIView?,viewAreaHeight:CGFloat) -> CGFloat
{
let contentSize = self.contentSize

var offset:CGFloat = 0.0
let subviewRect = view != nil ? view!.convert(view!.bounds, to: self) : CGRect.zero

var padding = (viewAreaHeight - subviewRect.height)/2
if padding < kMinimumScrollOffsetPadding
{
padding = kMinimumScrollOffsetPadding
}

offset = subviewRect.origin.y - padding - self.contentInset.top

if offset > (contentSize.height - viewAreaHeight)
{
offset = contentSize.height - viewAreaHeight
}

if offset < -self.contentInset.top
{
offset = -self.contentInset.top
}

return offset
}

func TPKeyboardAvoiding_contentInsetForKeyboard() -> UIEdgeInsets
{
let state = self.keyboardAvoidingState()
var newInset = self.contentInset;

let keyboardRect = state.keyboardRect
newInset.bottom = keyboardRect.size.height - max(keyboardRect.maxY - self.bounds.maxY, 0)

return newInset

}

func TPKeyboardAvoiding_viewIsValidKeyViewCandidate(_ view:UIView)->Bool
{
if view.isHidden || !view.isUserInteractionEnabled {return false}

if view is UITextField
{
if (view as! UITextField).isEnabled {return true}
}

if view is UITextView
{
if (view as! UITextView).isEditable {return true}
}

return false
}

func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView, candidateView bestCandidate: inout UIView?)
{
let priorFrame = self.convert(priorView.frame, to: priorView.superview)
let candidateFrame = bestCandidate == nil ? CGRect.zero : self.convert(bestCandidate!.frame, to: bestCandidate!.superview)

var bestCandidateHeuristic = -sqrt(candidateFrame.origin.x*candidateFrame.origin.x + candidateFrame.origin.y*candidateFrame.origin.y) + ( Float(fabs(candidateFrame.minY - priorFrame.minY))<Float.ulpOfOne ? 1e6 : 0)

for childView in view.subviews
{
if TPKeyboardAvoiding_viewIsValidKeyViewCandidate(childView)
{
let frame = self.convert(childView.frame, to: view)
let heuristic = -sqrt(frame.origin.x*frame.origin.x + frame.origin.y*frame.origin.y)
+ (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne ? 1e6 : 0)

if childView != priorView && (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne
&& frame.minX > priorFrame.minX
|| frame.minY > priorFrame.minY)
&& (bestCandidate == nil || heuristic > bestCandidateHeuristic)
{
bestCandidate = childView
bestCandidateHeuristic = heuristic
}
}else
{
self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &bestCandidate)
}
}
}

func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView) ->UIView?
{
var candidate:UIView?
self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &candidate)
return candidate
}


@objc func TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_ obj: AnyObject)
{
func processWithView(_ view: UIView) {
for childView in view.subviews
{
if childView is UITextField || childView is UITextView
{
self.TPKeyboardAvoiding_initializeView(childView)
}else
{
self.TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(childView)
}
}
}

if let timer = obj as? Timer, let view = timer.userInfo as? UIView {
processWithView(view)
}
else if let view = obj as? UIView {
processWithView(view)
}
}

func TPKeyboardAvoiding_initializeView(_ view:UIView)
{
if let textField = view as? UITextField,
let delegate = self as? UITextFieldDelegate, textField.returnKeyType == UIReturnKeyType.default &&
textField.delegate !== delegate
{
textField.delegate = delegate
let otherView = self.TPKeyboardAvoiding_findNextInputViewAfterView(view, beneathView: self)
textField.returnKeyType = otherView != nil ? .next : .done

}
}

func keyboardAvoidingState()->TPKeyboardAvoidingState
{
var state = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as? TPKeyboardAvoidingState
if state == nil
{
state = TPKeyboardAvoidingState()
self.state = state
}

return self.state!
}

}

// MARK: - Internal object observer
internal class TPKeyboardAvoidingState:NSObject
{
var priorInset = UIEdgeInsets.zero
var priorScrollIndicatorInsets = UIEdgeInsets.zero

var keyboardVisible = false
var keyboardRect = CGRect.zero
var priorContentSize = CGSize.zero

var priorPagingEnabled = false
}

internal extension UIScrollView
{
fileprivate struct AssociatedKeysKeyboard {
static var DescriptiveName = "KeyBoard_DescriptiveName"
}

var state:TPKeyboardAvoidingState?{
get{
let optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as AnyObject?
if let object:AnyObject = optionalObject {
return object as? TPKeyboardAvoidingState
} else {
return nil
}
}
set{
objc_setAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}

关于ios - 如何在swift项目中使用TPKeyboardAvoiding?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35625451/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com