UIInputViewController NSAutoresizingMaskLayoutConstraint issue - ios

I keep trying to implement input accessory VC in my app and I faced with the following issue. When I'm trying to modify the height of the root view of my custom UIInputViewController it's working well despite the one problem. The problem is that in the logs I see the following:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
<NSAutoresizingMaskLayoutConstraint:0x174490cc0 UIInputView:0x10a80bb80.(null) == 46.5>,
<NSAutoresizingMaskLayoutConstraint:0x17448cd50 UIInputView:0x10a80bb80.height == 76>,
<NSLayoutConstraint:0x17429b710 UIInputView:0x10a80bb80.top == UIInputSetHostView:0x10402e940.top>
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x17429b710 UIInputView:0x10a80bb80.top == UIInputSetHostView:0x10402e940.top>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Code of my custom UIInputViewController:
import UIKit
import RxSwift
import RxCocoa
#objc protocol InputViewControllerDelegate: NSObjectProtocol {
func answerTextViewDidChange(_ textView: UITextView)
}
class InputViewController: UIInputViewController {
fileprivate var closeButton: UIButton?
private var separatorView: UIView?
private var tipLabel: UILabel?
private var answerTextView: ConstrainedTextView?
private var buttonHeightConstraint: NSLayoutConstraint?
private var separatorHeightConstraint: NSLayoutConstraint?
private var answerTextViewBottomConstraint: NSLayoutConstraint?
weak var delegate: InputViewControllerDelegate?
private let junk = DisposeBag()
private var appropriateMaxLines: Int {
let isPortrait = UIDevice.current.orientation.isPortrait
return isPortrait ? 5 : 3
}
var answerText: String {
get {
return answerTextView?.text ?? ""
}
set {
answerTextView?.text = newValue
}
}
var isAnswerTextViewFirstResponder: Bool {
get {
return answerTextView?.isFirstResponder ?? false
}
set {
_ = newValue ? answerTextView?.becomeFirstResponder() : answerTextView?.resignFirstResponder()
}
}
// MARK: - Life Cycle
deinit {
NotificationCenter.default.removeObserver(self)
}
override func loadView() {
super.loadView()
let notifName = Notification.Name.UIDeviceOrientationDidChange
NotificationCenter.default.addObserver(self,
selector: #selector(rotated),
name: notifName,
object: nil)
configureView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
answerTextView?.maxLines = appropriateMaxLines
setCloseButtonDisabledIfNeeded()
}
// MARK: - Layout
private func configureView() {
view.backgroundColor = RGB(0xF6F6F6)
view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: 70)
view.autoresizingMask = [.flexibleWidth]
// Separator
separatorView = UIView()
separatorView?.backgroundColor = UIColor.lightGray
separatorView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(separatorView!)
AutoLayoutEqualizeSuper(separatorView, .left, 0)
AutoLayoutEqualizeSuper(separatorView, .right, 0)
AutoLayoutEqualizeSuper(separatorView, .top, 0)
separatorHeightConstraint = AutoLayoutSetAttribute(separatorView, .height, 1)
// Close Button
closeButton = UIButton(type: .system)
closeButton?.setTitle("Hide", for: .normal)
closeButton?.titleLabel?.font = UIFont.systemFont(ofSize: 17)
closeButton?.translatesAutoresizingMaskIntoConstraints = false
closeButton?.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
view.addSubview(closeButton!)
AutoLayoutSetAttribute(closeButton, .width, 70)
buttonHeightConstraint = AutoLayoutSetAttribute(closeButton, .height, 35)
AutoLayoutEqualizeSuper(closeButton, .right, -5)
view.addConstraint(NSLayoutConstraint(item: closeButton!, attribute: .top, relatedBy: .equal, toItem: separatorView, attribute: .bottom, multiplier: 1, constant: 0))
// Tip Label
tipLabel = UILabel()
tipLabel?.textColor = UIColor.darkGray
tipLabel?.text = "Your answer:"
tipLabel?.font = UIFont.systemFont(ofSize: 17)
tipLabel?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tipLabel!)
AutoLayoutEqualizeSuper(tipLabel, .left, 5)
AutoLayoutSetAttribute(tipLabel, .height, 35)
view.addConstraint(NSLayoutConstraint(item: tipLabel!, attribute: .right, relatedBy: .equal, toItem: closeButton, attribute: .left, multiplier: 1, constant: 0))
// Text View
answerTextView = ConstrainedTextView()
answerTextView?.backgroundColor = UIColor.white
answerTextView?.delegate = self
answerTextView?.scrollsToTop = false
answerTextView?.showsVerticalScrollIndicator = false
answerTextView?.font = REG_FONT(15)
answerTextView?.translatesAutoresizingMaskIntoConstraints = false
answerTextView?.layer.masksToBounds = true
answerTextView?.layer.cornerRadius = 7
answerTextView?.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.7).cgColor
answerTextView?.layer.borderWidth = 1
view.addSubview(answerTextView!)
AutoLayoutEqualizeSuper(answerTextView, .left, 5)
AutoLayoutEqualizeSuper(answerTextView, .right, -5)
answerTextViewBottomConstraint = AutoLayoutEqualizeSuper(answerTextView, .bottom, -5)
answerTextView?
.rx
.observe(CGRect.self, "bounds")
.distinctUntilChanged {
$0?.height == $1?.height
}
.subscribe(onNext: { [unowned self] newBounds in
if var newHeight = newBounds?.height,
let separatorHeight = self.separatorHeightConstraint?.constant,
let buttonHeight = self.buttonHeightConstraint?.constant,
let bottomSpace = self.answerTextViewBottomConstraint?.constant {
newHeight = newHeight < 35 ? 35 : newHeight
let generalHeight = newHeight + separatorHeight + buttonHeight + abs(bottomSpace)
var frame = self.view.frame
frame.size.height = generalHeight
self.view.frame = frame
}
})
.addDisposableTo(junk)
}
// MARK: - Other methods
fileprivate func setCloseButtonDisabledIfNeeded() {
closeButton?.isEnabled = answerTextView?.isFirstResponder ?? false
}
func rotated() {
answerTextView?.maxLines = appropriateMaxLines
}
}
// MARK: - UITextViewDelegate Protocol Conformance
extension InputViewController: UITextViewDelegate {
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
textView.inputAccessoryView = view
return true
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
textView.inputAccessoryView = nil
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
setCloseButtonDisabledIfNeeded()
}
func textViewDidEndEditing(_ textView: UITextView) {
setCloseButtonDisabledIfNeeded()
}
func textViewDidChange(_ textView: UITextView) {
delegate?.answerTextViewDidChange(textView)
}
}

Related

Show activity indicator in UICollectionView Footer

How to show and hide activity indicator in collection-view footer. I need to add a activity indicator in footer area of collectionview.
Sample for you:
import UIKit
open class PagingTableView: UITableView {
private var loadingView: UIView!
private var indicator: UIActivityIndicatorView!
internal var page: Int = 0
internal var previousItemCount: Int = 0
open var currentPage: Int {
get {
return page
}
}
open weak var pagingDelegate: PagingTableViewDelegate? {
didSet {
pagingDelegate?.paginate(self, to: page)
}
}
open var isLoading: Bool = false {
didSet {
isLoading ? showLoading() : hideLoading()
}
}
open func reset() {
page = 0
previousItemCount = 0
pagingDelegate?.paginate(self, to: page)
}
private func paginate(_ tableView: PagingTableView, forIndexAt indexPath: IndexPath) {
let itemCount = tableView.dataSource?.tableView(tableView, numberOfRowsInSection: indexPath.section) ?? 0
guard indexPath.row == itemCount - 1 else { return }
guard previousItemCount != itemCount else { return }
page += 1
previousItemCount = itemCount
pagingDelegate?.paginate(self, to: page)
}
private func showLoading() {
if loadingView == nil {
createLoadingView()
}
tableFooterView = loadingView
}
private func hideLoading() {
reloadData()
pagingDelegate?.didPaginate?(self, to: page)
tableFooterView = nil
}
private func createLoadingView() {
loadingView = UIView(frame: CGRect(x: 0, y: 0, width: frame.width, height: 50))
indicator = UIActivityIndicatorView()
indicator.color = UIColor.lightGray
indicator.translatesAutoresizingMaskIntoConstraints = false
indicator.startAnimating()
loadingView.addSubview(indicator)
centerIndicator()
tableFooterView = loadingView
}
private func centerIndicator() {
let xCenterConstraint = NSLayoutConstraint(
item: loadingView, attribute: .centerX, relatedBy: .equal,
toItem: indicator, attribute: .centerX, multiplier: 1, constant: 0
)
loadingView.addConstraint(xCenterConstraint)
let yCenterConstraint = NSLayoutConstraint(
item: loadingView, attribute: .centerY, relatedBy: .equal,
toItem: indicator, attribute: .centerY, multiplier: 1, constant: 0
)
loadingView.addConstraint(yCenterConstraint)
}
override open func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell {
paginate(self, forIndexAt: indexPath)
return super.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
}
}

IBDesignable element misplaced in interface builder, needs to update frames manually

I'm working on a Radio Button View, and already uploaded a test project on github at https://github.com/paulicelli/RadioButton
This is basically a subclass of a UIStackView, with a IBInspectable number of buttons to create some radio buttons.
Working on the device, but I always see a misplacement of the buttons when I open Interface Builder, until I change the number of buttons or the spacing. You can see the problem in view1, then the correct view in view2 attached.
QUESTION:
How can I tell Interface builder to properly position the buttons as soon as I open it?
here is the code:
import UIKit
// ROUNDBUTTON
final class RoundButton: UIButton {
var borderWidth: CGFloat = 2.0
var primaryColor: UIColor = UIColor.blue
var primaryColorWithAlpha: UIColor {
return primaryColor.withAlphaComponent(0.25)
}
override public var buttonType: UIButtonType {
return .custom
}
override public func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = bounds.size.width * 0.5
self.clipsToBounds = true
setupColors()
}
func setupColors() {
switch self.state {
case UIControlState.normal:
super.backgroundColor = .white
self.setTitleColor(primaryColor, for: state)
self.layer.borderColor = primaryColor.cgColor
self.layer.borderWidth = borderWidth
default:
super.backgroundColor = primaryColorWithAlpha
self.setTitleColor(.white, for: state)
}
}
}
// END OF ROUNDBUTTON
// PROTOCOL
#objc protocol RadioButtonsViewDelegate {
#objc optional func didSelectButton(_ aButton: RoundButton?)
}
// END OF PROTOCOL
// RADIOBUTTONSVIEW
#IBDesignable
class RadioButtonsView: UIStackView {
var shouldLetDeselect = true
var buttons = [RoundButton]()
weak var delegate : RadioButtonsViewDelegate? = nil
private(set) weak var currentSelectedButton: RoundButton? = nil
#IBInspectable
var number: Int = 2 {
didSet {
number = max(number, 2)
setupButtons()
}
}
func setupButtons() {
if !buttons.isEmpty {
buttons.forEach { self.removeArrangedSubview($0) }
buttons.removeAll()
}
for i in 0..<self.number {
let button = RoundButton()
button.setTitle("\(i)", for: .normal)
button.addTarget(self, action: #selector(pressed(_:)),
for: UIControlEvents.touchUpInside)
buttons.append(button)
}
}
func pressed(_ sender: RoundButton) {
if (sender.isSelected) {
if shouldLetDeselect {
sender.isSelected = false
currentSelectedButton = nil
}
}else {
buttons.forEach { $0.isSelected = false }
sender.isSelected = true
currentSelectedButton = sender
}
delegate?.didSelectButton?(currentSelectedButton)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupButtons()
}
required init(coder: NSCoder) {
super.init(coder: coder)
setupButtons()
}
override var intrinsicContentSize: CGSize {
return CGSize(width: self.bounds.width,
height: UIViewNoIntrinsicMetric)
}
override func layoutSubviews() {
self.translatesAutoresizingMaskIntoConstraints = false
buttons.forEach { self.addArrangedSubview($0) }
let constraints: [NSLayoutConstraint] = {
var constraints = [NSLayoutConstraint]()
constraints.append(NSLayoutConstraint(item: buttons[0],
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: self,
attribute: NSLayoutAttribute.height,
multiplier: 1,
constant: 0)
)
for i in 1..<buttons.count {
constraints.append(NSLayoutConstraint(item: buttons[i],
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: buttons[i - 1],
attribute: NSLayoutAttribute.width,
multiplier: 1,
constant: 0)
)
}
return constraints
}()
self.addConstraints(constraints)
}
}
// END OF RADIOBUTTONSVIEW
EDIT:
As requested by #dfd I also tried:
#IBInspectable
var number: Int {
get {
setupButtons()
return self.number
}
set {
self.number = max(newValue, 2)
setupButtons()
}
}
and I got 2 errors while loading Interface Builder:
error: IB Designables: Failed to render and update auto layout status for ViewController (BYZ-38-t0r): The agent crashed
error: IB Designables: Failed to update auto layout status: The agent crashed

UIGestureRecognizer not detected on subview

I made a UIView subclass and added a circle view on top right corner of the view. Then I added UIPanGestureRecognizer to the circle view.
The problem is that gesture is only recognized on left bottom part of circle where the circle is located over the super view.
How can I make entire circle to property detected gesture?
Following is entire class code of UIView subclass I made.
import UIKit
class ResizableImageView: UIView {
private let circleWidth: CGFloat = 40
var themeColor: UIColor = UIColor.magentaColor()
lazy var cornerCircle: UIView = {
let v = UIView()
v.layer.cornerRadius = self.circleWidth / 2
v.layer.borderWidth = 1
v.layer.borderColor = self.themeColor.CGColor
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(buttonTouchMoved) )
v.addGestureRecognizer(panGesture)
return v
}()
// Init
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
configureSelf()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
// Add cornerButton to self and set auto layout
addSubview( cornerCircle )
addConstraintsWithFormat("H:[v0(\(circleWidth))]", views: cornerCircle) // Extension code for setting auto layout
addConstraintsWithFormat("V:[v0(\(circleWidth))]", views: cornerCircle)
addConstraint(NSLayoutConstraint(item: cornerCircle, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: cornerCircle, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
}
func configureSelf() {
// Set border
layer.borderWidth = 1
layer.borderColor = themeColor.CGColor
}
// Gesture Event
func buttonTouchMoved(gestureRecognizer: UIPanGestureRecognizer) {
let point = gestureRecognizer.locationInView(self)
print(point)
}
}
ViewController
import UIKit
class ImageViewCheckController: UIViewController {
let imageView: ResizableImageView = {
let iv = ResizableImageView()
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "ImageViewCheck"
view.backgroundColor = UIColor.whiteColor()
setupSubviews()
}
func setupSubviews() {
view.addSubview( imageView )
view.addConstraintsWithFormat("V:|-100-[v0(200)]", views: imageView)
view.addConstraintsWithFormat("H:|-50-[v0(100)]", views: imageView)
}
}
Normally, there is no touch on a subview outside the bounds of its superview.
To change this, you will have to override hitTest(point:withEvent:) on the superview to alter the way hit-testing works:
override func hitTest(point: CGPoint, withEvent e: UIEvent?) -> UIView? {
if let result = super.hitTest(point, withEvent:e) {
return result
}
for sub in self.subviews.reverse() {
let pt = self.convertPoint(point, toView:sub)
if let result = sub.hitTest(pt, withEvent:e) {
return result
}
}
return nil
}

How to scroll text inside uitextfield iOS

I need to scroll data inside UITextfield so that the text of longer width can be seen by scrolling.
Can someone reply me to solve this?
You can use UITextView: Try below one.
UITextView *textView=[[UITextView alloc] initWithFrame:CGRectMake(20, 40, 200, 70)];
textView.font=[UIFont systemFontOfSize:18.0];
textView.userInteractionEnabled=YES;
textView.backgroundColor=[UIColor whiteColor];
textView.scrollEnabled=YES;
textView.delegate = self;
[self.view addSubview:textView];
I add the textField on a UIScrollView, and change the contentSize when the length of the text changed, and scroll the scrollView when the cursor's position changed or selection range changed.
I have made a demo:
private enum TextRangeChangedType: Int {
case leftAndBack = 0
case leftAndForward
case rightAndBack
case rightAndForward
case none
}
public class ScrollableTextField: UIView {
/// Real textFiled.
///
/// You should set delegate, add actions or resign first responder to this view.
private(set) var textField: UITextField?
// MARK: - Private
private let oneCutWidth: CGFloat = UIScreen.main.bounds.width
private let defaultCutTimes: CGFloat = 3
private var scrollView: UIScrollView?
private weak var tapGesture: UITapGestureRecognizer?
private weak var textFiledWidthConstraint: NSLayoutConstraint?
// MARK: - Private funcs
private func configureSubviews() {
//scroll
scrollView = UIScrollView(frame: .zero)
if let scrollView = scrollView {
scrollView.contentSize = CGSize(width: oneCutWidth * defaultCutTimes, height: 0)
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.bounces = false
scrollView.delegate = self
scrollView.backgroundColor = .clear
self.addSubview(scrollView)
// if not using masonry
scrollView.translatesAutoresizingMaskIntoConstraints = false
let scrollLeftConstraint = NSLayoutConstraint(item: scrollView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0)
let scrollRightConstraint = NSLayoutConstraint(item: scrollView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0)
let scrollTopConstraint = NSLayoutConstraint(item: scrollView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0)
let scrollBottomConstraint = NSLayoutConstraint(item: scrollView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
self.addConstraints([scrollLeftConstraint, scrollRightConstraint, scrollTopConstraint, scrollBottomConstraint])
// scrollView.mas_makeConstraints {[weak self] (make) in // if use masonry
// make?.edges.equalTo()(self)
// }
//text field
textField = InnerTextField(frame: .zero)
let oneCut = oneCutWidth
let times = defaultCutTimes
if let textField = textField as? InnerTextField {
textField.addTarget(self, action: #selector(handleTextField(_:)), for: .editingChanged)
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
gesture.delegate = self
textField.addGestureRecognizer(gesture)
tapGesture = gesture
textField.selectedTextRangeChangedBlock = {[weak self] (type, width) in
guard let scrollView = self?.scrollView else {
return
}
let div: CGFloat = 15
if width < scrollView.contentOffset.x + div {
UIView.animate(withDuration: 0.1, animations: {
scrollView.contentOffset.x = max(width - div, 0)
})
} else if width > scrollView.contentOffset.x + scrollView.bounds.width - div {
UIView.animate(withDuration: 0.1, animations: {
scrollView.contentOffset.x = width - scrollView.bounds.width + div
})
}
}
scrollView.addSubview(textField)
// if not using masonry
textField.translatesAutoresizingMaskIntoConstraints = false
let textLeftConstaint = NSLayoutConstraint(item: textField, attribute: .left, relatedBy: .equal, toItem: scrollView, attribute: .left, multiplier: 1, constant: 0)
let textTopConstrait = NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0)
let textBottomConstrait = NSLayoutConstraint(item: textField, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
let textWidthConstrait = NSLayoutConstraint(item: textField, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: oneCut * times)
scrollView.addConstraint(textLeftConstaint)
self.addConstraints([textTopConstrait, textBottomConstrait])
textField.addConstraint(textWidthConstrait)
textFiledWidthConstraint = textWidthConstrait
// textField.mas_makeConstraints {[weak self] (make) in // if use masonry
// make?.left.equalTo()(scrollView)
// make?.top.and()?.bottom()?.equalTo()(self)
// make?.width.equalTo()(oneCut * times)
// }
}
}
}
// MARK: - Life circle
public override init(frame: CGRect) {
super.init(frame: frame)
configureSubviews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Actions
#objc private func handleTap(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: textField)
let cloestPosition = textField?.closestPosition(to: point)
if let cloestPosition = cloestPosition {
textField?.selectedTextRange = textField?.textRange(from: cloestPosition, to: cloestPosition)
}
}
#objc private func handleTextField(_ textField: UITextField) {
guard let scrollView = self.scrollView, let hookedTF = textField as? InnerTextField else {
return
}
guard let width = hookedTF.getWidthFromDocumentBeginingToCursor(), let fullWidth = hookedTF.getWidthFromDocumentBeginingToEnd() else {
return
}
let selfWidth = self.bounds.width
if selfWidth == 0 {
return
}
//check max bounds
if scrollView.contentSize.width - fullWidth < oneCutWidth {
if scrollView.contentSize.width <= fullWidth {
scrollView.contentSize.width = fullWidth + oneCutWidth
} else {
scrollView.contentSize.width += oneCutWidth
}
// if not using masonry
textFiledWidthConstraint?.constant = scrollView.contentSize.width
// textField.mas_updateConstraints { (make) in // if use masonry
// make?.width.equalTo()(scrollView.contentSize.width)
// }
self.layoutIfNeeded()
}
if width >= selfWidth - 3 {
if width - scrollView.contentOffset.x >= 0 && width - scrollView.contentOffset.x < selfWidth {
return
}
let diff = max(width - selfWidth + 3, 0)
scrollView.contentOffset.x = diff
} else {
scrollView.contentOffset.x = 0
}
}
}
// MARK: - Delegate
extension ScrollableTextField: UIScrollViewDelegate, UIGestureRecognizerDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let currentTextWidth = (textField as? InnerTextField)?.getWidthFromDocumentBeginingToEnd() else {
return
}
let selfFrame = self.frame
if currentTextWidth < selfFrame.width {
scrollView.contentOffset.x = 0
return
}
let maxOffsetX = currentTextWidth - selfFrame.width + 6
if scrollView.contentOffset.x > maxOffsetX {
scrollView.contentOffset.x = maxOffsetX
}
}
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == self.tapGesture {
if self.textField?.isFirstResponder ?? false {
return true
} else {
return false
}
}
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
}
// MARK: - Private class
private class InnerTextField: UITextField {
// MARK: - Public
func getWidthFromDocumentBeginingToCursor() -> CGFloat? {
guard let selectedRange = self.selectedTextRange else {
return nil
}
let width = getWidthFromDocumentBegining(to: selectedRange.start)
return width
}
func getWidthFromDocumentBeginingToEnd() -> CGFloat? {
guard let str = self.text else {
return nil
}
let width = getWidthFrom(string: str)
return width
}
// MARK: - Private
private func changeType(oldRange: UITextRange, newRange: UITextRange) -> TextRangeChangedType {
let oldStart = self.offset(from: beginningOfDocument, to: oldRange.start)
let oldEnd = self.offset(from: beginningOfDocument, to: oldRange.end)
let newStart = self.offset(from: beginningOfDocument, to: newRange.start)
let newEnd = self.offset(from: beginningOfDocument, to: newRange.end)
if (oldStart == newStart) && (oldEnd != newEnd) {
if (newEnd > oldEnd) {
return .rightAndForward
} else if (newEnd < oldEnd) {
return .rightAndBack
}
return .none
}
if (oldStart != newStart) && (oldEnd == newEnd) {
if (newStart < oldStart) {
return .leftAndBack
} else if (newStart > oldStart) {
return .leftAndForward
}
return .none
}
if (oldStart == oldEnd) && (newStart == newEnd) {
if newStart > oldStart {
return .rightAndForward
} else if newStart < oldStart {
return .leftAndBack
}
}
return .none
}
private func getWidthFrom(string text: String) -> CGFloat {
let label = UILabel(frame: .zero)
label.text = text
var defaultFont = UIFont.systemFont(ofSize: 15)
if let font = self.font {
defaultFont = font
}
label.font = defaultFont
label.sizeToFit()
let width = label.bounds.size.width
return width
}
private func getWidthFromDocumentBegining(to position: UITextPosition) -> CGFloat? {
if let textStr = self.text {
let curText = textStr as NSString
let offset = self.offset(from: beginningOfDocument, to: position)
guard offset <= curText.length && offset >= 0 else {
return nil
}
let subStr = curText.substring(to: offset)
let width = getWidthFrom(string: subStr)
return width
}
return nil
}
override var text: String? {
didSet {
self.sendActions(for: .editingChanged)
}
}
override var selectedTextRange: UITextRange? {
willSet {
if let old = selectedTextRange, let `new` = newValue {
let willChangeType = changeType(oldRange: old, newRange: new)
if willChangeType == .leftAndBack || willChangeType == .leftAndForward {
if let width = getWidthFromDocumentBegining(to: new.start) {
selectedTextRangeChangedBlock?(willChangeType, width)
}
} else if willChangeType == .rightAndForward || willChangeType == .rightAndBack {
if let width = getWidthFromDocumentBegining(to: new.end) {
selectedTextRangeChangedBlock?(willChangeType, width)
}
}
}
}
}
var selectedTextRangeChangedBlock: ((_ changType: TextRangeChangedType, _ beforeTextWidth: CGFloat) -> ())?
}
or you can have a preview on my github: https://github.com/sunshuyao/ScrollableTextField
You can do it by this way.
UITextView *textField = [[UITextView alloc]initWithFrame:CGRectMake(100, 100, 60, 50)];
textField.text = #"I need to scroll data inside UITextfield so that the text of longer width can be seen by scrolling.Can someone reply me to solve this.";
textField.delegate = self;
[self.view addSubview:textField];

How to update uiview size when orientation is changed?

The view loads with a height of 324 pixels. Then view updates its height to 250 pixels when I turn the device landscape, but after I turn the device from landscape to portrait it doesn't reset the height of the view back to 324 pixels.
import UIKit
class KeyboardViewController: UIInputViewController {
var calcBoard = CalcControl()
var disNum:Double = 0.0
var expandedHeight:CGFloat = 250
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
//self.view.addConstraint(heightconstraints2)
var heightconstraints3:NSLayoutConstraint = NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.0, constant: self.expandedHeight)
//self.view.addConstraint(heightconstraints2)
if toInterfaceOrientation == UIInterfaceOrientation.LandscapeLeft{
self.expandedHeight = 250
self.view.addConstraint(heightconstraints3) }
if toInterfaceOrientation == UIInterfaceOrientation.LandscapeRight{
self.expandedHeight = 250
self.view.addConstraint(heightconstraints3)
}
if toInterfaceOrientation == UIInterfaceOrientation.Portrait{
println("In portrait")
println("Transforming")
self.expandedHeight = 324
self.view.addConstraint(heightconstraints3)
}
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
if fromInterfaceOrientation == UIInterfaceOrientation.Portrait{
println("Adjusting...")
self.expandedHeight = 324
}
}
override func viewDidLoad() {
super.viewDidLoad()
println("Loaded")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "LoadNumber:", name: "load", object: nil)
self.deleteBackward()
// Perform custom UI setup here
//self.calcBoard.frame = CGRect(x: 123, y: 0, width: 320, height: 324)
var xibView1 = NSBundle.mainBundle().loadNibNamed("CalcView", owner: nil, options: nil)
self.calcBoard = xibView1[0] as CalcControl
self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 324)
self.view.addSubview(calcBoard)
}
func LoadNumber(notification:NSNotification){
println("got it")
var num = (calcBoard.number as NSNumber).stringValue
println(num)
var proxy = textDocumentProxy as UITextDocumentProxy
proxy.insertText(num)
}
func recieveNumberfromSource()->Double{
return self.disNum
}
func deleteBackward(){
}
func hasText() -> Bool{
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(textInput: UITextInput) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
var proxy = self.textDocumentProxy as UITextDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
}
override func viewDidAppear(animated: Bool) {
//self.updateViewConstraints()
var expandedHeight:CGFloat = 324
var heightconstraint:NSLayoutConstraint = NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.0, constant: expandedHeight)
self.view.addConstraint(heightconstraint)
}
}
Keep a reference to the heightConstraint when you first add it and update the constant of it when the orientation changes.
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
if UIInterfaceOrientationIsLandscape(toInterfaceOrientation) {
heightConstraint.constant = 100
} else {
heightConstraint.constant = 300
}
}
An assignment to self.expandedHeight will not change the constraint after its creation.

Resources