I have uiview with colored border. I want to add a subview above, so it "hide" parent view border. Currently when i try to add a view (subclass of UILabel) above it's not overlap anything as i want. What i want is remove white line when it interact with label frame.
My class is:
class LabeledContainerView: UIView {
var text: String!
var height: CGFloat!
var offset: CGFloat!
var label: UILabel = {
let lbl = LabelSL.create(textColor: Theme.Color.white,
font: Theme.Font.regular())
lbl.text = Strings.login.value
lbl.backgroundColor = Theme.Color.clear.value
return lbl
}()
init(text: String,
height: Double,
offset: Double) {
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
self.text = text
self.height = CGFloat(height)
self.offset = CGFloat(offset)
createUI()
setConstraints()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func highlight(){
}
func turnOffHighlight(){
}
private func createUI(){
translatesAutoresizingMaskIntoConstraints = false
clipsToBounds = false
layer.cornerRadius = 4.0
layer.borderColor = UIColor.white.cgColor
layer.borderWidth = 2.0
addSubview(label)
}
private func setConstraints(){
let tinyOffset: CGFloat = 2
label.leftAnchor.constraint(equalTo: leftAnchor, constant: offset + tinyOffset).isActive = true
label.centerYAnchor.constraint(equalTo: topAnchor).isActive = true
}
}
You can do that without adding another view above it.
By set your textfield delegate to self then
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text?.isEmpty ?? false{
//yourView.hideBorders
}
}
If i didn't got your point please clarify
You have set lbl.backgroundColor = Theme.Color.clear.value.
Thats why you can see the border. Set background color of label same as your view background color.
You need to ensure two things are happening:
The UILabel is in front of the UITextField in the view hierarchy
This can be set either in the XIB file (storyboard) or programatically:
In the XIB depending on the where the view is in the list determines its priority. The higher it is in the list the further back it is. Therefore you want to move your label to be below your UITextField. You can drag and drop it in the left hand list.
You can also set this programatically by pushing your textField to the back of the hierarchy:
sendSubviewToBack(UITextField)
The UILabel has a background colour
This can also be set programatically or in the XIB:
Programatically:
label.backgroundColor = .red
XIB:
Checking these two things will ensure that the UILabel is in front of the UITextField and covers the border when it has text.
Related
Help me in one of the two ways maybe:
How to solve the problem? or
How to understand the error message?
Project summary
So I'm learning about inputAccessoryView by making a tiny project, which has only one UIButton. Tapping the button summons the keyboard with inputAccessoryView which contains 1 UITextField and 1 UIButton. The UITextField in the inputAccessoryView will be the final firstResponder that is responsible for the keyboard with that inputAccessoryView
The error message
API error: <_UIKBCompatInputView: 0x7fcefb418290; frame = (0 0; 0 0); layer = <CALayer: 0x60000295a5e0>> returned 0 width, assuming UIViewNoIntrinsicMetric
The code
is very straightforward as below
The custom UIView is used as inputAccessoryView. It installs 2 UI outlets, and tell responder chain that it canBecomeFirstResponder.
class CustomTextFieldView: UIView {
let doneButton:UIButton = {
let button = UIButton(type: .close)
return button
}()
let textField:UITextField = {
let textField = UITextField()
textField.placeholder = "placeholder"
return textField
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
initSetup()
}
override init(frame:CGRect) {
super.init(frame: frame)
initSetup()
}
convenience init() {
self.init(frame: .zero)
}
func initSetup() {
addSubview(doneButton)
addSubview(textField)
}
func autosizing(to vc: UIViewController) {
frame = CGRect(x: 0, y: 0, width: vc.view.frame.size.width, height: 40)
let totalWidth = frame.size.width - 40
doneButton.frame = CGRect(x: totalWidth * 4 / 5 + 20,
y: 0,
width: totalWidth / 5,
height: frame.size.height)
textField.frame = CGRect(x: 20,
y: 0,
width: totalWidth * 4 / 5,
height: frame.size.height)
}
override var canBecomeFirstResponder: Bool { true }
override var intrinsicContentSize: CGSize {
CGSize(width: 400, height: 40)
} // overriding this variable seems to have no effect.
}
Main VC uses the custom UIView as inputAccessoryView. The UITextField in the inputAccessoryView becomes the real firstResponder in the end, I believe.
class ViewController: UIViewController {
let customView = CustomTextFieldView()
var keyboardShown = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
customView.autosizing(to: self)
}
#IBAction func summonKeyboard() {
print("hello")
keyboardShown = true
self.becomeFirstResponder()
customView.textField.becomeFirstResponder()
}
override var canBecomeFirstResponder: Bool { keyboardShown }
override var inputAccessoryView: UIView? {
return customView
}
}
I've seen people on the internet says this error message will go away if I run on a physical phone. I didn't go away when I tried.
I override intrinsicContentSize of the custom view, but it has no effect.
The error message shows twice together when I tap summon.
What "frame" or "layer" does the error message refer to? Does it refer to the custom view's frame and layer?
If we use Debug View Hierarchy we can see that _UIKBCompatInputView is part of the (internal) view hierarchy of the keyboard.
It's not unusual to see constraint errors / warnings with internal views.
Since frame and/or intrinsic content size seem to have no effect, I don't think it can be avoided (nor does it seem to need to be).
As a side note, you can keep the "Done" button round by using auto-layout constraints. Here's an example:
class CustomTextFieldView: UIView {
let textField: UITextField = {
let tf = UITextField()
tf.font = .systemFont(ofSize: 16)
tf.autocorrectionType = .no
tf.returnKeyType = .done
tf.placeholder = "placeholder"
// textField backgroundColor so we can see its frame
tf.backgroundColor = .yellow
return tf
}()
let doneButton:UIButton = {
let button = UIButton(type: .close)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
autoresizingMask = [.flexibleHeight, .flexibleWidth]
[doneButton, textField].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
}
NSLayoutConstraint.activate([
// constrain doneButton
// Trailing: 20-pts from trailing
doneButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
// Top and Bottom 8-pts from top and bottom
doneButton.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
doneButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
// Width equal to default height
// this will keep the button round instead of oval
doneButton.widthAnchor.constraint(equalTo: doneButton.heightAnchor),
// constrain textField
// Leading: 20-pts from leading
textField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
// Trailing: 8-pts from doneButton leading
textField.trailingAnchor.constraint(equalTo: doneButton.leadingAnchor, constant: -8.0),
// vertically centered
textField.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
}
class CustomTextFieldViewController: UIViewController {
let customView = CustomTextFieldView()
var keyboardShown = false
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func summonKeyboard() {
print("hello")
keyboardShown = true
self.becomeFirstResponder()
customView.textField.becomeFirstResponder()
}
override var canBecomeFirstResponder: Bool { keyboardShown }
override var inputAccessoryView: UIView? {
return customView
}
}
I have two UILabel with (orange and pink one) one is multiline label and other one is just one label. Two of this component (which has same texts) is inside UIStackView and this Stackview is inside UITableView (Automatic dimension). My problem is that whether I set the same text to UILabels their heights look different. I'm searching for this problem for two days but none of solutions work for me.
Ambiguous layout warnings for UILabels in UITableViewCell
Label1 has top, left, right layout and Label2 has top,left,right,bottom.
UIStackView distribution is .fillProportionally
I want the labels has same height If they are same for example;
For View1;
Label1 = "Multiline Label 1 Text which is two line"
Label2 = "One Line Label 2 text"
For View2;
Label1 = "Multiline Label 1 Text which is two line"
Label2 = "One Line Label 2 text"
What I want is: view1 and view2 height should be same because they have same elements with same content In them. Stackview which has this view1 and view2 has fill proportionally option should set Its height according to view1 + view2 height.
Container view which gets a xib file (It is just a xib file which has two label in it with the constraints I mention earlier).
class ViewWithLabel: UIView {
#IBOutlet weak var firstLabel: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet var containerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
// first: load the view hierarchy to get proper outlets
let name = String(describing: type(of: self))
let nib = UINib(nibName: name, bundle: .main)
nib.instantiate(withOwner: self, options: nil)
addSubview(containerView)
containerView.frame = self.bounds
containerView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}
convenience init(firstLabelText: String, secondLabelText: String) {
self.init()
firstLabel.text = firstLabelText
secondLabel.text = secondLabelText
}
override func layoutSubviews() {
super.layoutSubviews()
firstLabel.preferredMaxLayoutWidth = self.frame.width
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Another container view which has a Stackview and gets the above view and add to Stackview.
class ViewWithStack: UIView {
let verticalStackView: UIStackView = {
let s = UIStackView()
s.distribution = .fillEqually
s.spacing = 0
s.axis = .vertical
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.white
self.layer.cornerRadius = 6.0
self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)
addSubview(verticalStackView)
verticalStackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
let bottomEqual = verticalStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
bottomEqual.isActive = true
verticalStackView.leftAnchor.constraint(equalTo: self.leftAnchor,constant: 0).isActive = true
verticalStackView.rightAnchor.constraint(equalTo: self.rightAnchor,constant: 0).isActive = true
verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
verticalStackView.isLayoutMarginsRelativeArrangement = true
}
convenience init(orientation: NSLayoutConstraint.Axis,labelsArray: [UIView]) {
self.init()
verticalStackView.axis = orientation
for label in labelsArray {
verticalStackView.addArrangedSubview(label)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
EDIT: If I write firstLabel.sizeToFit() in layoutSubviews function. First label looks fixed but content view still has same height and there is a big gap between label 1 and label 2.
EDIT 2: When I add setContentHuggingPriority(.required, for: .vertical) for labels. First label looks okay but second label looks bigger too.
I'm trying to create a view like below using storyboard in Xcode.
For this, I've added a button and a label with constraints but this is the result I get. The text doesn't start below the checkbox. One way to achieve this would be to create 2 labels and add the strings that start after this string to 2 label and place it under the first label.
Is there any better way to do this? Also, when you click on Read more the text can expand as well.
You can make the leading of the button and the label the same ( button above label ) and insert some empty characters at the beginning of the label text
You can do a way. Take the button and the label in a view
then sub divide the view into two views, left one holds the button and right one holds the label. make a gap between left and right
button's constraint will be leading , top and trailing to zero and height as your wish
label's constraint will be leading , top, trailing and bottom.
You can accomplish this by using a UITextView and setting an ExclusionPath.
The ExclusionPath (or paths) defines an area within the text view's textContainer around which the text should wrap.
If you disable scrolling, selection and editing of the UITextView it will behave just like a UILabel but will also give you the benefit of ExclusionPath
Here is a simple example - lots of hard-coded values, so you'd probably want to make it a custom class - but this should get you on your way:
class TextViewLabel: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
isScrollEnabled = false
isEditable = false
isSelectable = false
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
class ExclusionViewController: UIViewController {
let checkBox: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let theTextViewLabel: TextViewLabel = {
let v = TextViewLabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .yellow
v.text = "I agree to receive information about this application and all the updates related to this..."
v.font = UIFont.systemFont(ofSize: 20.0)
return v
}()
var isChecked: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(theTextViewLabel)
theTextViewLabel.addSubview(checkBox)
let cbWidth = CGFloat(20)
NSLayoutConstraint.activate([
theTextViewLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
theTextViewLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0),
theTextViewLabel.widthAnchor.constraint(equalToConstant: 240.0),
checkBox.topAnchor.constraint(equalTo: theTextViewLabel.topAnchor, constant: 2.0),
checkBox.leadingAnchor.constraint(equalTo: theTextViewLabel.leadingAnchor, constant: 0.0),
checkBox.widthAnchor.constraint(equalToConstant: cbWidth),
checkBox.heightAnchor.constraint(equalToConstant: cbWidth),
])
theTextViewLabel.textContainer.exclusionPaths = [
UIBezierPath(rect: CGRect(x: 0, y: 0, width: cbWidth + 8.0, height: cbWidth))
]
updateCheckboxImage()
checkBox.addTarget(self, action: #selector(checkBoxTapped), for: .touchUpInside)
}
func updateCheckboxImage() -> Void {
if isChecked {
checkBox.setImage(UIImage(named: "SmallChecked"), for: .normal)
} else {
checkBox.setImage(UIImage(named: "SmallUnChecked"), for: .normal)
}
}
#objc func checkBoxTapped() -> Void {
isChecked = !isChecked
updateCheckboxImage()
}
}
Result:
(I used these two images for the checkBox):
I have a keyboardContainer class (Subclass of UIView / created programmatically so no storyboard) including a UITextView for the user to type messages in. It is used within a Chat log class and set as the inputAccessoryView. I want to dynamically change the height of it when the user is typing.
I searched for answers and found some. However, I didn't get most of them as they didn't work for me.
What do I have to implement to get the effect I want to have?
Thank´s for your help!
EDIT:
First of all thank you for your help!
However, I´m pretty new to coding so I could not solve the issue. I guess it has something to do with the way I created my keyboardContainer class and its constraints...
Here is the relevant code from within my keyboard container class:
let textField:UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 15
view.layer.masksToBounds = true
view.font = UIFont.systemFont(ofSize: 15)
view.backgroundColor = .white
return view
}()
overried init(frame: CGRect){
super.init(frame: frame)
addSubview(textField)
textField.leftAnchor.constraint(equalTo: leftButton, constant: 5).isActive = true
textField.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
textFieldHeightAnchor = textField.heightAnchor.constraint(equalTo: heightAnchor, constant: -10)
textFieldHeightAnchor.isActive = true
textFieldRightAnchor = textField.rightAnchor.constraint(equalTo: rightAnchor, constant: -85)
textFieldRightAnchor.isActive = true
}
Inside my ChatLog I´m using this:
lazy var keyboard: KeyboardContainer = {
let key = KeyboardContainer()
key.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 45)
key.sendButton.addTarget(self, action: #selector(handleSend), for: .touchUpInside)
return key
}()
override var inputAccessoryView: UIView?{
get{
return keyboard
}
}
What do I need to change ? I guess the constraints?
You can make your textView conform to the UITextViewDelegate and then resize it to it's contentSize.
Make your view controller conform to the delegate
class ViewController: UIViewController, **UITextViewDelegate** { ...
After that
yourTextView.delegate = self // put that in viewDidLoad()
Then you can implement the textViewDidChange method. That means, every time you enter something into the keyboard, this function is called.
func textViewDidChange(_ textView: UITextView) {
if textView == youTextView {
let currentHeight = textView.frame.size.height
textView.frame.size.height = 0 // you have to do that because if not it's not working with the proper content size
textView.frame.size = textView.contentSize // here you detext your textView's content size and make it resize.
let newHeight = textView.frame.size.height
let heightDifference = newHeight - currentHeight // get the height difference from before and after editing
yourContainerView.frame.size.height += heightDifference
}
}
Just disable the scrolling. Don't set any other constraints. This might help you with this.
In your viewDidLoad method,
YourTextView.translatesAutoresizingMaskIntoConstraints = true
YourTextView.sizeToFit()
YourTextView.isScrollEnabled = false
YourTextView.delegate = self
YourTextView.isEditable = true
Then use UITextViewDelegate and,
func textViewDidChange(_ textView: UITextView) {
MyTextView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 100)
MyTextView.translatesAutoresizingMaskIntoConstraints = true
MyTextView.sizeToFit()
MyTextView.isScrollEnabled = false
}
This was before the change,
This was after,
If you are looking for UITextView like any message app.
No need to deal with code you can manage it using constraints only.
Seems a better way to do this using constraints rather than deal with more coding.
Here is the step by step explanation of this.
Step : 1
Arrange UItextView and UIBUtton inside one UIView and give height constraint to UIView with low priority.
Step : 2
Also give height constraint to UITextView with Greater Then or Equal. as image represent it.
Step : 3
Now you just have to deal with contentSize and isScrollEnabled as below snippet.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool{
if (textView.contentSize.height < 150){
textView.isScrollEnabled = false
}
else{
textView.isScrollEnabled = true
}
return true
}
Hope this help you!!
I have created a .xib with a UISearchBar.
I am looking to remove the top and bottom borders as seen in this image:
There is also what looks like a shadow underneath the top border.
I have tried changing backgrounds/tint colours etc, but struggling to get it to disappear.
Any help would be welcome, any ideas?
EDIT
So the code below allows me to get rid of the border at the top and bottom, but not the weird drop shadow. I've added green background to show it more clearly.
Also, what would be the best way to add a border around the text box (rather than the view)?? Thanks
//** This is what I'm trying to do: https://stackoverflow.com/questions/33107058/uiviewcontroller-sizing-as-maincontroller-within-uisplitviewcontroller
and here is the code I am using.
import UIKit
class TopSearchViewController: UIView {
var expanded: Int = 0
#IBOutlet weak var trailingConstraint: NSLayoutConstraint!
#IBOutlet var contentView: UIView!
#IBOutlet weak var searchBar: UISearchBar!
#IBAction func advancedSearchButton(sender: AnyObject) {
if expanded == 0 {
self.layoutIfNeeded()
UIView.animateWithDuration(0.1, animations: {
self.trailingConstraint.constant = 200
self.layoutIfNeeded()
})
expanded = 1
} else {
self.layoutIfNeeded()
UIView.animateWithDuration(0.1, animations: {
self.trailingConstraint.constant = 25
self.layoutIfNeeded()
})
expanded = 0
}
}
override init(frame: CGRect) { // for using CustomView in code
super.init(frame: frame)
self.commonInit()
}
required init(coder aDecoder: NSCoder) { // for using CustomView in IB
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
NSBundle.mainBundle().loadNibNamed("TopSearchViewController", owner: self, options: nil)
contentView.frame = self.bounds
contentView.autoresizingMask = .FlexibleHeight | .FlexibleWidth
self.addSubview(contentView)
self.searchBar.layer.borderWidth = 1
self.searchBar.layer.borderColor = UIColor.whiteColor().CGColor
println(searchBar)
for subview in searchBar.subviews {
println("hello")
var textField : UITextField
if (subview.isKindOfClass(UITextField)) {
textField = subview as! UITextField
textField.borderStyle = .None
textField.layer.borderWidth = 1
textField.layer.borderColor = UIColor.lightGrayColor().CGColor
textField.layer.cornerRadius = 14
textField.background = nil;
textField.backgroundColor = UIColor.whiteColor()
}
}
}
func viewDidLoad() {
}
}
Set the bar's backgroundImage to an empty UIImage, not nil:
searchBar.backgroundImage = UIImage()
You may also set the barTintColor, as you specified in your comment below.
With Xcode 11, in the Storyboard, you can set 'Search Style' to 'Minimal.'
In code: searchBar.searchBarStyle = .minimal
I have tried all answers, but they didn't remove the 1px inner shadow of UISearchBar's UITextfield inside.
I solved this problem by increasing white border's width.
It overlaps on shadow.
searchBar.layer.borderWidth = 10
searchBar.layer.borderColor = UIColor.white.cgColor
# Method 1
With Thanking to #andrewlundy, Storyboard method is like this. (Xcode 11.*)
Select UISearch at the storyboard (assumed: you have already dragged an UISearch Bar to your UIView/UIViewController)
Goto Attribute Inspector and you will see Search Style
Select Minimal from Search Style dropdown.
That's it...
# Method 2
Else if you want to do this programmatically, in swift
create an IBOutlet for UISearch in your ViewController swift file
searchBar.searchBarStyle = .minimal
here instead of searchBar, put your UISearch outlet's name
Try this:
searchBar.layer.borderWidth = 1
searchBar.layer.borderColor = UIColor.whiteColor().CGColor
EDIT:
I believe that shadow is caused by inner UITextField. Try with below code:
var textField : UITextField
for subview in searchBar.subviews {
if (subview.isKindOfClass(UITextField)) {
textField = subview as! UITextField
textField.borderStyle = .None
textField.layer.borderWidth = 1
textField.layer.borderColor = UIColor.lightGrayColor().CGColor
textField.layer.cornerRadius = 14
textField.background = nil;
textField.backgroundColor = UIColor.whiteColor()
}
}
There's another trick:
For the bottom border, you can cover it by bellow view( for example: tableView) by -1 pixel.
For the top border, move it up 1 pixel to under navigation bar also fix this.
I used following code in swift5 to implement searchbar
List item
let searchbar: UISearchBar =
{
let sear = UISearchBar()
sear.tintColor = Mycolor().lightgray1
sear.backgroundColor = Mycolor().lightgray1
sear.barTintColor = Mycolor().lightgray1
sear.placeholder = "Search Asset"
sear.layer.cornerRadius = 30
sear.barStyle = .default
sear.backgroundImage = UIImage()
// sear.addShadow(offset: CGSize(width: 10, height: 10), color: UIColor.darkGray, radius: 20, opacity: 5)
return sear
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(searchbar)
}
override func viewLayoutMarginsDidChange() {
self.searchbar.frame = CGRect(x: 10, y: 20, width: self.view.frame.width - 20, height: 50)
}
guard let searchTextField = searchBar.value(forKey: "searchField") as? UITextField else { return }
searchTextField.borderStyle = .none
if you want to remove the border completely
Swift 5
Try searchTextField property for minimum deployment target iOS 13 in case other suggested options not working for you.
if #available(iOS 13.0, *) {
searchBar.searchTextField.backgroundColor = UIColor.clear
searchBar.searchTextField.borderStyle = .none
}