I have a circleButton, and adding a gesture recognizer to it is not working. circleButton is a UIView.
#IBOutlet private weak var circleButton: UIView!
Here is my code:
class CustomBottomBar: UIView {
#IBOutlet private weak var circleButton: UIView!
#IBOutlet private weak var bottomBarView: UIView!
#IBOutlet private weak var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
circleButton.layer.borderColor = UIColor.black.cgColor
circleButton.layer.borderWidth = 2
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
#objc func onAddButtonClicked() {
print("add")
}
private func commonInit() {
if let customBar = Bundle.main.loadNibNamed("CustomBottomBar", owner: self, options: nil)?[0] as? UIView {
customBar.frame = bounds
addSubview(customBar)
circleButton.layer.cornerRadius = circleButton.frame.height/2
circleButton.clipsToBounds = true
circleButton.isUserInteractionEnabled = true
print(self.circleButton.frame.size)
print(self.contentView.frame.size)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onAddButtonClicked))
circleButton.addGestureRecognizer(tapGesture)
}
}
}
The frame size is also coming properly for circleButton. Not sure what is the problem. I am using customBottomBar in a storyboard. Here is the full code :
https://github.com/hirakjyotiborah/AwesomeTaskManag3r
Inside your Main.storyboard give a height constraint to your bottom Bar as you only set leading , trailing and bottom or do this inside commonInit
// 70 (Bottom bar) + 80/2 (half of circle button) = 110
heightAnchor.constraint(equalToConstant: 110).isActive = true
As the base for responding to any click is the frame and the main parent view of the button has zero height
Related
I am creating a custom UIView in swift, here is the code for the custom View,
class FormField: UIView {
#IBOutlet var viewLabel : UILabel!
#IBOutlet var textField: UITextField!
#IBOutlet var separator: UIView!
private var verticallyCenteredAnchor : NSLayoutConstraint!
private var topPositionedAnchor : NSLayoutConstraint!
private var isViewHighlighted = false
private var view: UIView?
let nibName = "FormField"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
self.view = view
view.frame = self.bounds
self.addSubview(view)
verticallyCenteredAnchor = viewLabel.centerYAnchor.constraint(equalTo: self.view!.centerYAnchor)
verticallyCenteredAnchor.isActive = false
topPositionedAnchor = viewLabel.topAnchor.constraint(equalTo: self.view!.topAnchor,constant: 0)
topPositionedAnchor.isActive = true
let userNameTap = UITapGestureRecognizer(target: self, action: #selector(animateLabel))
userNameTap.numberOfTapsRequired = 1
view.addGestureRecognizer(userNameTap)
}
#objc func animateLabel() {
if(self.isViewHighlighted) {
verticallyCenteredAnchor.isActive = true
topPositionedAnchor.isActive = false
} else {
verticallyCenteredAnchor.isActive = false
topPositionedAnchor.isActive = true
}
self.isViewHighlighted = !self.isViewHighlighted
UIView.animate(withDuration: 0.5) {
self.view!.layoutIfNeeded()
}
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
I am using a xib file for the view and constraints are set in that. Now when someone taps on the view I am trying to change the constraints with animation. But that's not working, no layout change is happening when the user taps on the view. I can confirm that the animateLabel() method is called when the user taps.
Now if is add in the line,
self.view!.translatesAutoresizingMaskIntoConstraints = false
all constraints are messed up, it's not honouring the constraints I already set in the xib.
What's going wrong here?
I am using a xib file for the view and constraints are set in that.
You have conflicts in your constraints who are in xib + in code , you need to hook the constraints in xib as outlets and play with their .isActive property and completely remove the code constraints
I am trying to create a verification process in 4 steps, in order to make my code more efficient, I decided to use Child views and just update the UI accordingly.
I managed to add my child view to my MasterView, however, I am unable to click the button inside my child view.
I already checked the view hierarchy and there is nothing on top of my button. I also tried to add the action programmatically, re-added the button but I can't make it work. I am new to swift development so probably I am missing something.
Child view Code
protocol IdentityVerificationIntroChildViewControllerDelegate{
func startVIProcess(_ sender: Any)
}
class IdentityVerificationIntroChildViewController: UIView{
#IBOutlet var contentView: UIView!
var delegate: IdentityVerificationIntroChildViewControllerDelegate?
#IBOutlet weak var mStartVIBtn: UIButton!
override init(frame: CGRect){
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
commonInit()
}
private func commonInit(){
Bundle.main.loadNibNamed("IdentityVerificationIntroChildView", owner: self, options: nil)
mStartVIBtn.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
#objc func buttonAction(sender: UIButton!) {
print("works")
}
}
Master view code
final class MasterIdentityVerificationViewController: UIViewController {
#IBOutlet weak var mContainerView: UIView!
#IBOutlet weak var mStepIndicator: StepIndicatorView!
private var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
setChildView(subView: IdentityVerificationIntroChildViewController())
}
private func setChildView(subView: UIView){
currentView?.removeFromSuperview()
currentView = subView
currentView?.translatesAutoresizingMaskIntoConstraints = false
guard let currentView = currentView else { return }
mContainerView.addSubview(currentView)
NSLayoutConstraint.activate([
currentView.topAnchor.constraint(equalTo: mContainerView.topAnchor),
currentView.trailingAnchor.constraint(equalTo: mContainerView.trailingAnchor),
currentView.leadingAnchor.constraint(equalTo: mContainerView.leadingAnchor),
currentView.bottomAnchor.constraint(equalTo: mContainerView.bottomAnchor)
])
}
}
extension MasterIdentityVerificationViewController: IdentityVerificationIntroChildViewControllerDelegate{
func startVIProcess(_ sender: Any) {
performSegue(withIdentifier: "fromVIIntroToVIIDCapture", sender: sender)
print("adfsdfs")
}
}
View Hierarchy, the problematic button is Highlighted
Green Area is where Child views are getting switched
I would really appreciate any help. Thanks
first I create a .xib file and named it RadioBtnView and connect it to a custom UIView class :
class CheckBtnView: UIView {
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var CheckBtn: UIButton!
#IBOutlet weak var checkDescription: UILabel!
var status = false {
didSet {
if status {
CheckBtn.backgroundColor = .orange
} else {
CheckBtn.backgroundColor = .clear
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
private func setupViews(){
Bundle.main.loadNibNamed("CheckBtnView", owner: self, options: nil)
addSubview(mainView)
mainView.frame = self.bounds
mainView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
}
}
then try to add an instance of RadioBtnView programmatically in viewController and add gesture recognizer to it :
let radio = RadioBtnView()
scrollContainerView.addSubview(radio)
radio.translatesAutoresizingMaskIntoConstraints = false
radio.topAnchor.constraint(equalTo: entry.bottomAnchor, constant: 15).isActive = true
radio.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 38).isActive = true
radio.isUserInteractionEnabled = true
let radioGesture = UITapGestureRecognizer(target: self, action: #selector(change(_:)))
radio.addGestureRecognizer(radioGesture)
but the change(_:) function doesn't respond seems like it didn't call
is this for creating the xib view programmatically or something else?
I finally know the reason ... I just delete radio.translatesAutoresizingMaskIntoConstraints = false and add the radio.frame = CGRect(x:50,y:100,width:120,height:20) and every think work
this seems like we can not add xib view programmatically
Created a custom UIView 'MyLabelView' with two elements, 1x UILabel, 1x UIView which will be set to a background color.
MyLabelView
View:
Code:
import UIKit
#IBDesignable
class MyLabelView: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var imageView: UIView!
#IBOutlet weak var titleLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
contentView?.prepareForInterfaceBuilder()
}
private func commonInit() {
let bundle = Bundle(for: type(of: self))
bundle.loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)
addSubview(contentView)
}
}
I'm using MyLabelView on two occasions:- MasterViewController (TableViewController with dynamic prototypes)- DetailViewController (TableViewController with static cells and grouped style)
MasterViewController
Shows MyLabelView correct as part of a custom UITableViewCellView:
Code (in cellForRowAt):
let cell: MyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell") as! MyTableViewCell
cell.myLabelView.titleLabel.text = "Label"
cell.myLabelView.imageView.backgroundColor = UIColor.red
// Dynamic width to fit label texts of various sizes
var frm: CGRect = cell.myLabelView.titleLabel.frame
frm.size.width = cell.myLabelView.titleLabel.intrinsicContentSize.width
cell.myLabelView.titleLabel.frame = frm
DetailViewController
Here I would like to render MyLabelView with increased font size (working) and right-aligned so that no matter how long the Label text is, it is always aligned next to the chevronView:
Code (in viewDidLoad):
myLabelView.titleLabel.text = "Label"
myLabelView.titleLabel.font = myLabelView.titleLabel.font.withSize(17)
myLabelView.imageView.backgroundColor = UIColor.red
// Dynamic width to fit label texts of various sizes
var frm: CGRect = myLabelView.titleLabel.frame
frm.size.width = myLabelView.titleLabel.intrinsicContentSize.width
myLabelView.titleLabel.frame = frm
Expected results
View:
View:
I tried to play around with additional, outer UIViews as containers and using AutoLayout, but somehow it never worked as I expected it. Thank you very much for your help.Swift 4, iOS11
UPDATE
I tried to set the constraints in IB as I don't have access to the cell (using static cells).Content View:
Title:
MyLabelView:
Just add a function to the UITableViewCell subclass and call it when you need to set the view next to the right side:
func setTrailingAnchorForView() {
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: imageView.leadingAnchor, constant: 0).isActive = true
}
Call it like this:
cell.setTrailingAnchorForView()
I want to add a custom view in TableViewHeader. But when I run the following code it creates a Cycle and app stuck for any user interaction.
import UIKit
class ExpandableView: UIView {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var userNamelbl: UILabel!
#IBOutlet weak var countLbl: UILabel!
#IBOutlet weak var rightArrowImgView: UIImageView!
var isExpanded = false
var contentView: UIView!
#IBOutlet weak var checkMarkView: UIImageView!
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
public override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
private func setUpView() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "ExpandableView", bundle: bundle)
self.contentView = nib.instantiate(withOwner: self, options: nil).first as? UIView
addSubview(contentView)
contentView.center = self.center
contentView.autoresizingMask = []
contentView.translatesAutoresizingMaskIntoConstraints = true
}
}
I am using it as follows:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let frame = CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 60)
let expandabelView = ExpandableView(frame: frame)
return expandabelView
}
And it shows following error on run.
There may be many other ways, but I recommend you to make your ExpandableView reusable to improve performance.
First of all, simplify your ExpandableView class:
import UIKit
class ExpandableView: UITableViewHeaderFooterView {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var userNamelbl: UILabel!
#IBOutlet weak var countLbl: UILabel!
#IBOutlet weak var rightArrowImgView: UIImageView!
var isExpanded = false
#IBOutlet weak var checkMarkView: UIImageView!
}
Please do not miss that the superclass is UITableViewHeaderFooterView, not UIView.
Second, check the settings of your ExpandableView.xib:
The Custom View setting of the defined view needs to be ExpandableView.
When you cannot choose ExpandableView from the pull down list, you may need to input manually. Do not forget to check Inherit Module From Target.
The Custom View setting of the File's Owner needs to be empty.
If there's some class already set, remove it manually.
Confirm all Outlets are connected properly to your ExpandableView.
(You should better reconnect them all, after you modified your xib.)
You may need to re-structure your view hierarchy and/or constraints.
Third, modify your view controller holding the table view.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
//...
let nib = UINib(nibName: "ExpandableView", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "ExpandableView")
tableView.estimatedSectionHeaderHeight = 60
tableView.sectionHeaderHeight = UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let expandabelView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "ExpandableView")
// Frame size should be represented with constraints.
return expandabelView
}
When you create custom UIView, it should follow this if you want to use it init with frame.
class CustomView: UIView {
//This should be contentview of your xib
#IBOutlet var view: UIView!
let nibName = "CustomView"
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
private func xibSetup()
{
Bundle.main.loadNibNamed(nibName, owner: self, options: nil)
self.view.autoresizesSubviews = true
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.view)
self.view.frame = self.frame
}
}
Since you need to load view from nib, just load it and then add subview to your view. Then set content view's frame and set autoresizing mask of content view correctly
private func setUpView() {
Bundle.main.loadNibNamed("ExpandableView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}