Views centered in parent below each other - swift

I'm trying to make this view with the Layout extension, I tried a little bit, but can't figure it out.
This is my code so far:
import UIKit
import Material
class ViewController: UIViewController {
private var nameField: TextField!
private var emailField: ErrorTextField!
private var passwordField: TextField!
private let constant: CGFloat = 32
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Color.indigo.base
prepareNameField()
preparePasswordField()
prepareResignResponderButton()
}
/// Prepares the resign responder button.
private func prepareResignResponderButton() {
let btn = FlatButton(title: "Login", titleColor: Color.white)
btn.addTarget(self, action: #selector(handleResignResponderButton(button:)), for: .touchUpInside)
view.layout(btn).width(100).height(constant).right(0).top(8 * constant).horizontally(left: constant, right: constant);
}
/// Handle the resign responder button.
#objc
internal func handleResignResponderButton(button: UIButton) {
nameField?.resignFirstResponder()
passwordField?.resignFirstResponder()
}
private func prepareNameField() {
nameField = TextField()
nameField.placeholderNormalColor = Color.indigo.lighten4
nameField.placeholderActiveColor = Color.white
nameField.dividerNormalColor = Color.indigo.lighten4
nameField.dividerActiveColor = Color.white
nameField.isClearIconButtonEnabled = true
nameField.textColor = Color.white
nameField.placeholder = "Username"
view.layout(nameField).top(4 * constant).horizontally(left: constant, right: constant)
}
private func preparePasswordField() {
passwordField = TextField()
passwordField.placeholderNormalColor = Color.indigo.lighten4
passwordField.placeholderActiveColor = Color.white
passwordField.dividerNormalColor = Color.indigo.lighten4
passwordField.dividerActiveColor = Color.white
passwordField.isClearIconButtonEnabled = true
passwordField.textColor = Color.white
passwordField.placeholder = "Password"
passwordField.clearButtonMode = .whileEditing
passwordField.isVisibilityIconButtonEnabled = true
// Setting the visibilityIconButton color.
passwordField.visibilityIconButton?.tintColor = Color.white.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54)
view.layout(passwordField).top(6 * constant).horizontally(left: constant, right: constant)
}
}
I'm new to swift so if someone can explain me how to accomplish it that would be great.

Here is an example with TextFields.
import UIKit
import Material
class ViewController: UIViewController {
fileprivate var emailField: ErrorTextField!
fileprivate var passwordField: TextField!
/// A constant to layout the textFields.
fileprivate let constant: CGFloat = 32
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Color.grey.lighten5
preparePasswordField()
prepareEmailField()
prepareResignResponderButton()
}
/// Prepares the resign responder button.
fileprivate func prepareResignResponderButton() {
let btn = RaisedButton(title: "Resign", titleColor: Color.blue.base)
btn.addTarget(self, action: #selector(handleResignResponderButton(button:)), for: .touchUpInside)
view.layout(btn).width(100).height(constant).centerVertically(offset: emailField.height / 2 + 60).right(20)
}
/// Handle the resign responder button.
#objc
internal func handleResignResponderButton(button: UIButton) {
emailField?.resignFirstResponder()
passwordField?.resignFirstResponder()
}
}
extension ViewController {
fileprivate func prepareEmailField() {
emailField = ErrorTextField()
emailField.placeholder = "Email"
emailField.detail = "Error, incorrect email"
emailField.isClearIconButtonEnabled = true
emailField.delegate = self
view.layout(emailField).center(offsetY: -passwordField.height - 60).left(20).right(20)
}
fileprivate func preparePasswordField() {
passwordField = TextField()
passwordField.placeholder = "Password"
passwordField.detail = "At least 8 characters"
passwordField.clearButtonMode = .whileEditing
passwordField.isVisibilityIconButtonEnabled = true
// Setting the visibilityIconButton color.
passwordField.visibilityIconButton?.tintColor = Color.green.base.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54)
view.layout(passwordField).center().left(20).right(20)
}
}
extension UIViewController: TextFieldDelegate {
public func textFieldDidEndEditing(_ textField: UITextField) {
(textField as? ErrorTextField)?.isErrorRevealed = false
}
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
return true
}
}

As you can see below, with the visual formatting language, you're basically painting with code.
import UIKit
class ViewController: UIViewController {
var view1:UILabel!
var view2:UILabel!
var view3:UILabel!
override func viewDidLoad() {
super.viewDidLoad()
initViews();
initLayouts();
}
func initViews() {
view.backgroundColor = UIColor.white
view1 = createLabel(title: "view1")
view2 = createLabel(title: "view2")
view3 = createLabel(title: "view3")
view.addSubview(view1)
view.addSubview(view2)
view.addSubview(view3)
}
func initLayouts() {
for view in view.subviews {
view.translatesAutoresizingMaskIntoConstraints = false
}
var views = [String: AnyObject]()
views["view1"] = view1
views["view2"] = view2
views["view3"] = view3
// ---------------------------------------------------------
// Setup horizontal constraints for all three views
// ---------------------------------------------------------
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view1]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view2(==view1)]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[view3]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
// special layout condition to make third view half the width of other views
view.addConstraint(NSLayoutConstraint(item: view3,
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: view1,
attribute: NSLayoutAttribute.width,
multiplier: 0.5,
constant: 0.0))
// ---------------------------------------------------------
// Setup vertical constraints for all three views
// ---------------------------------------------------------
view.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[view1]-20-[view2]-20-[view3]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
// make all three views equal height
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view1(44)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view2(==view1)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view3(==view1)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
}
// ------------------------------------------------
// Helper function
// ------------------------------------------------
func createLabel(title:String) -> UILabel {
let textLabel = UILabel()
textLabel.text = title
textLabel.backgroundColor = UIColor.red
textLabel.textColor = UIColor.black
textLabel.textAlignment = NSTextAlignment.center
textLabel.layer.borderColor = UIColor.black.cgColor
textLabel.layer.borderWidth = 2.0
return textLabel
}
}
You should see something like this:

Related

In keeping with the recent questions on closures used to pass data between VCs, how would I do the same when using a containerVC within the rootVC?

I saw a recent bountied question (can find the link if you wish to see it) about using closures to pass data between VCs where one VC was embedded in a navigation controller. While the use of a closure there was fairly easy since there was a direct point of contact between the two VCs (in the form a segue), I have been wondering how the same would work if this was not the case.
As an example, consider the following set up (similar to the OG question that inspired this post):
RootVC, which has a counter UILabel
A subContainer VC which takes up the lower half of RootVC, which has a button, pressing which should increment the UILabel on RootVC by one.
I have prepared the code as follows (with some code taken from the OG question):
RootVC:
class RootVC: UIViewController {
var tappedCount: Int = 0
let pagingContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var label: UILabel = {
let label = UILabel()
label.text = "\(tappedCount)"
label.textAlignment = .center
label.font = UIFont(name: "Copperplate", size: 90)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
view.addSubview(pagingContainer)
pagingContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
pagingContainer.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true
pagingContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
pagingContainer.heightAnchor.constraint(equalToConstant: 500).isActive = true
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
addChild(pageController)
pageController.didMove(toParent: self)
pageController.view.translatesAutoresizingMaskIntoConstraints = false
pagingContainer.addSubview(pageController.view)
pageController.view.heightAnchor.constraint(equalTo: pagingContainer.heightAnchor, multiplier: 1).isActive = true
pageController.view.widthAnchor.constraint(equalTo: pagingContainer.widthAnchor, multiplier: 1).isActive = true
pageController.view.topAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
pageController.view.bottomAnchor.constraint(equalTo: pagingContainer.bottomAnchor).isActive = true
pageController.view.leadingAnchor.constraint(equalTo: pagingContainer.leadingAnchor).isActive = true
pageController.view.trailingAnchor.constraint(equalTo: pagingContainer.trailingAnchor).isActive = true
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
}
}
SubContainerVC:
class SubContainerVC: UIViewController {
var callback : (() -> Void)?
let button: UIButton = {
let button = UIButton()
button.setTitle("Button!", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.backgroundColor = .green
return button
}()
#objc func buttonPressed(_ sender: UIButton) {
print("Hello")
//Pressing this button should increment the label on RootVC by one.
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
And the PageViewController swift file:
class PageViewController: UIPageViewController {
lazy var subViewControllers:[UIViewController] = {
return [SubContainerVC()]
}()
init(transitionStyle style:
UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
setViewControllerFromIndex(index: 0)
}
func setViewControllerFromIndex(index:Int) {
self.setViewControllers([subViewControllers[index]], direction: UIPageViewController.NavigationDirection.forward, animated: true, completion: nil)
}
}
extension PageViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return subViewControllers.count
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
if currentIndex <= 0 {
return nil
}
return subViewControllers[currentIndex-1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
if currentIndex >= subViewControllers.count-1 {
return nil
}
return subViewControllers[currentIndex+1]
}
}
You can inject the closure downstream to SubContainerVC, this will result in the closure execution coming up upstream.
Something along the lines (kept only the relevant VC code):
class SubContainerVC {
var buttonCallback: () -> Void = { }
#objc func buttonPressed(_ sender: UIButton) {
print("Hello")
buttonCallback()
}
}
class PageViewController: UIViewController {
// Note that you don't need the extra closure call for lazy vars
lazy var subViewControllers = [SubContainerVC()] {
didSet {
// just in case the controllers might change later on
subViewControllers.forEach { $0.buttonCallback = buttonCallback }
}
}
var buttonCallback: () -> Void = { } {
didSet {
subViewControllers.forEach { $0.buttonCallback = buttonCallback }
}
}
}
class RootVC: UIViewController {
var tappedCount: Int = 0 {
didSet {
label.text = "\(tappedCount)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
// this will trigger the `didSet` from PageViewController, resulting
// in the callback being propagated downstream
pageController.buttonCallback = { self.tappedCount += 1 }
}
}

UITextField is partially hidden by Keyboard when opened

I am attempting to create a collection of UITextField elements. I'd like the next button on the keyboard to skip to the next field and if that field is hidden from view by the keyboard, scroll it into view.
This is my attempt. It works apart from 1 aspect.
When dismissing the keyboard and then selecting another (or the same) field, the text input is partially hidden by the keyboard (see attached gif).
The meat and potatoes is within the ViewController extension.
class ViewController: UIViewController {
var activeField: UITextField?
var lastOffset: CGPoint!
var keyboardHeight: CGFloat!
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let scrollViewContainer: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 10
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(scrollViewContainer)
let totalFieldCount = 25
for i in 1...totalFieldCount {
let textField = createTextField(self, placeholder: "Field #\(i)", type: .default)
textField.returnKeyType = i < totalFieldCount ? .next : .done
textField.tag = i
scrollViewContainer.addArrangedSubview(textField)
}
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollViewContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
scrollViewContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
scrollViewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor),
scrollViewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
scrollViewContainer.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
scrollView.keyboardDismissMode = .interactive
}
func createTextField(_ delegate: UITextFieldDelegate?, placeholder: String, type: UIKeyboardType, isSecureEntry: Bool = false) -> UITextField {
let tf = UITextField(frame: .zero)
tf.placeholder = placeholder
tf.backgroundColor = .init(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = .systemFont(ofSize: 14)
tf.keyboardType = type
tf.autocapitalizationType = .none
tf.autocorrectionType = .no
tf.isSecureTextEntry = isSecureEntry
tf.heightAnchor.constraint(equalToConstant: 40).isActive = true
if let delegate = delegate {
tf.delegate = delegate
}
return tf
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
activeField = textField
lastOffset = self.scrollView.contentOffset
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
activeField?.resignFirstResponder()
activeField = nil
}
return true
}
}
extension ViewController {
#objc func keyboardWillShow(notification: NSNotification) {
guard keyboardHeight == nil else { return }
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
keyboardHeight = keyboardSize.height
UIView.animate(withDuration: 0.3, animations: {
self.scrollView.contentInset.bottom = self.keyboardHeight
})
guard let activeField = activeField else { return }
let distanceToBottom = self.scrollView.frame.size.height - (activeField.frame.origin.y) - (activeField.frame.size.height)
let collapseSpace = keyboardHeight - distanceToBottom
guard collapseSpace > 0 else { return }
UIView.animate(withDuration: 0.3, animations: {
self.scrollView.contentOffset = CGPoint(x: self.lastOffset.x, y: collapseSpace + 10)
})
}
}
#objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.scrollView.contentOffset = self.lastOffset
self.scrollView.contentInset.bottom = 0
}
keyboardHeight = nil
}
}
Replace keyboardFrameBeginUserInfoKey with keyboardFrameEndUserInfoKey

How to display a PopoverViewController from a custom Navigation Bar Right Button?

I created a custom Navigation Bar class as illustrated below, which I used across multiple ViewControllers:
import UIKit
import ChameleonFramework
class CustomUINavigationBar: UINavigationBar {
let navigationBarRightButtonView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(rightNavBarButtonTitleForNormalState: String, rightNavBarButtonImageForNormalState: String, rightNavBarButtonImageForHighlightedState: String, rightNavBarButtonTarget: Any?, rightNavBarButtonSelector: Selector, isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String, normalStateNavBarLeftButtonImage: String, highlightedStateNavBarLeftButtonImage: String, navBarLeftButtonTarget: Any?, navBarLeftButtonSelector: Selector, labelTitleText: String, titleLabelFontHexColourCode: String, labelTitleFontSize: CGFloat, labelTitleFontType: String) {
self.init()
addNavBarRightButton(rightNavBarButtonTitleForNormalState: rightNavBarButtonTitleForNormalState, rightNavBarButtonImageForNormalState: rightNavBarButtonImageForNormalState, rightNavBarButtonImageForHighlightedState: rightNavBarButtonImageForHighlightedState, rightNavBarButtonTarget: rightNavBarButtonTarget, rightNavBarButtonSelector: rightNavBarButtonSelector)
addNavBarLeftButton(normalStateNavBarLeftButtonImage: normalStateNavBarLeftButtonImage, highlightedStateNavBarLeftButtonImage: highlightedStateNavBarLeftButtonImage, navBarLeftButtonTarget: navBarLeftButtonTarget, navBarLeftButtonSelector: navBarLeftButtonSelector)
setupNavigationBarEssentials(isNavBarTranslucent: isNavBarTranslucent, navBarBackgroundColourHexCode: navBarBackgroundColourHexCode, navBarBackgroundColourAlphaValue: navBarBackgroundColourAlphaValue, navBarStyle: navBarStyle, preferLargeTitles: preferLargeTitles, navBarDelegate: navBarDelegate, navBarItemsHexColourCode: navBarItemsHexColourCode)
addTitleLabel(labelTitleText: labelTitleText, titleLabelFontHexColourCode: titleLabelFontHexColourCode, labelTitleFontSize: labelTitleFontSize, labelTitleFontType: labelTitleFontType)
}
let customNavigationBarItem = UINavigationItem()
func addTitleLabel(labelTitleText titleText: String, titleLabelFontHexColourCode hexCode: String, labelTitleFontSize fontSize: CGFloat, labelTitleFontType fontType: String) {
let navBarTitle = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: 44))
navBarTitle.text = titleText
navBarTitle.textColor = UIColor(hexString: hexCode)
navBarTitle.textAlignment = .center
navBarTitle.font = UIFont(name: fontType, size: fontSize)
navBarTitle.numberOfLines = 0
navBarTitle.lineBreakMode = .byWordWrapping
customNavigationBarItem.titleView = navBarTitle
}
func addNavBarLeftButton(normalStateNavBarLeftButtonImage: String, highlightedStateNavBarLeftButtonImage: String, navBarLeftButtonTarget: Any?, navBarLeftButtonSelector: Selector) {
let navBarLeftButton: UIButton = {
let button = UIButton()
let normalStateNavBarLeftButtonImage = UIImage(named: normalStateNavBarLeftButtonImage)
let highlightedStateNavBarLeftButtonImage = UIImage(named: highlightedStateNavBarLeftButtonImage)
button.setImage(normalStateNavBarLeftButtonImage, for: .normal)
button.setImage(highlightedStateNavBarLeftButtonImage, for: .highlighted)
button.addTarget(navBarLeftButtonTarget, action: navBarLeftButtonSelector, for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let navBarLeftView: UIView = {
let view = UIView()
view.addSubview(navBarLeftButton)
NSLayoutConstraint.activate([
navBarLeftButton.topAnchor.constraint(equalTo: view.topAnchor),
navBarLeftButton.rightAnchor.constraint(equalTo: view.rightAnchor),
navBarLeftButton.bottomAnchor.constraint(equalTo: view.bottomAnchor),
navBarLeftButton.leftAnchor.constraint(equalTo: view.leftAnchor)
])
return view
}()
let navBarLeftButtonItem = UIBarButtonItem(customView: navBarLeftView)
customNavigationBarItem.leftBarButtonItem = navBarLeftButtonItem
}
func addNavBarRightButton(rightNavBarButtonTitleForNormalState: String, rightNavBarButtonImageForNormalState: String, rightNavBarButtonImageForHighlightedState: String, rightNavBarButtonTarget: Any?, rightNavBarButtonSelector: Selector) {
rightNavigationBarDropDownButton.setTitle(rightNavBarButtonTitleForNormalState, for: .normal)
rightNavigationBarDropDownButton.setImage(UIImage(named: rightNavBarButtonImageForNormalState), for: .normal)
rightNavigationBarDropDownButton.setTitleColor(.black, for: .normal)
rightNavigationBarDropDownButton.setTitleColor(.blue, for: .highlighted)
rightNavigationBarDropDownButton.addTarget(rightNavBarButtonTarget, action: rightNavBarButtonSelector, for: .touchUpInside)
rightNavigationBarDropDownButton.translatesAutoresizingMaskIntoConstraints = false
navigationBarRightButtonView.addSubview(rightNavigationBarDropDownButton)
NSLayoutConstraint.activate([
rightNavigationBarDropDownButton.topAnchor.constraint(equalTo: navigationBarRightButtonView.topAnchor),
rightNavigationBarDropDownButton.rightAnchor.constraint(equalTo: navigationBarRightButtonView.rightAnchor),
rightNavigationBarDropDownButton.leftAnchor.constraint(equalTo: navigationBarRightButtonView.leftAnchor),
rightNavigationBarDropDownButton.bottomAnchor.constraint(equalTo: navigationBarRightButtonView.bottomAnchor)
])
let navigationBarRightViewitem = UIBarButtonItem(customView: navigationBarRightButtonView)
customNavigationBarItem.rightBarButtonItem = navigationBarRightViewitem
}
func setupNavigationBarEssentials(isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String) {
items = [customNavigationBarItem]
isTranslucent = isNavBarTranslucent
barTintColor = UIColor(hexString: navBarBackgroundColourHexCode, withAlpha: navBarBackgroundColourAlphaValue)
barStyle = navBarStyle
prefersLargeTitles = preferLargeTitles
delegate = navBarDelegate
tintColor = UIColor(hexString: navBarItemsHexColourCode)
translatesAutoresizingMaskIntoConstraints = false
}
}
I then created an instance from the above custom Navigation Bar class into the viewController where I would like the custom Navigation bar to show. Then I tried to present the PopoverViewController whenever the user taps on the NavBarRightButtonItem, however, nothing is showing up, could please someone help me figuring out where did I go wrong, thanks a lot?
import UIKit
class BlueBookUniversalBeamsVC: UIViewController, UINavigationBarDelegate, UIPopoverPresentationControllerDelegate {
lazy var navigationBar = CustomUINavigationBar(rightNavBarButtonTitleForNormalState: "Sort By:", rightNavBarButtonImageForNormalState: "pullDownButton", rightNavBarButtonImageForHighlightedState: "pullUpButton", rightNavBarButtonTarget: self, rightNavBarButtonSelector: #selector(navigationBarRightButtonPressed(sender:)), isNavBarTranslucent: false, navBarBackgroundColourHexCode: "#FFFFFF", navBarBackgroundColourAlphaValue: 1.0, navBarStyle: .black, preferLargeTitles: false, navBarDelegate: self, navBarItemsHexColourCode: "#FF4F40", normalStateNavBarLeftButtonImage: "normalStateBackButton", highlightedStateNavBarLeftButtonImage: "highlightedStateBackButton", navBarLeftButtonTarget: self, navBarLeftButtonSelector: #selector(navigationBarLeftButtonPressed(sender:)), labelTitleText: "Universal Beams (UB)", titleLabelFontHexColourCode: "#000000", labelTitleFontSize: 16, labelTitleFontType: "AppleSDGothicNeo-Light")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(navigationBar)
}
}
override func viewDidLayoutSubviews() {
setupConstraints()
}
#objc func navigationBarLeftButtonPressed(sender : UIButton) {
let viewControllerToGoTo = BlueBookTabController()
present(viewControllerToGoTo, animated: true, completion: nil)
}
#objc func navigationBarRightButtonPressed(sender : UIButton) {
let button = sender as? UIButton
let buttonFrame = button?.frame ?? CGRect.zero
let popoverContentController = self.storyboard?.instantiateViewController(withIdentifier: "PopoverViewController") as? PopoverViewController
popoverContentController?.modalPresentationStyle = .popover
if let popoverPresentationController = popoverContentController?.popoverPresentationController {
popoverPresentationController.permittedArrowDirections = .up
popoverPresentationController.sourceView = self.view
popoverPresentationController.sourceRect = buttonFrame
popoverPresentationController.delegate = self
if let popoverController = popoverContentController {
present(popoverController, animated: true, completion: nil)
}
}
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
}
func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return true
}
func position(for bar: UIBarPositioning) -> UIBarPosition {
return UIBarPosition.topAttached
}
func setupConstraints() {
NSLayoutConstraint.activate([
navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor),
navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor),
navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
}
}
I managed to sort out my problem using the below code, what confused me at the beginning is that I am using a Standalone NavigationBar rather than a NavigationBar Controller:
#objc func navigationBarRightButtonPressed(sender : UIButton) {
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let popOverViewController = storyboard.instantiateViewController(withIdentifier: "PopoverViewController")
popOverViewController.modalPresentationStyle = .popover
let popover = popOverViewController.popoverPresentationController!
popover.delegate = self
popover.permittedArrowDirections = .up
// The sourceView in the below code line represents the view containing the anchor rectangle for the popover:
popover.sourceView = navigationBar.navigationBarRightButtonView
// The sourceRect in the below code line represents The rectangle in the specified view in which to anchor the popover:
popover.sourceRect = navigationBar.navigationBarRightButtonView.bounds
present(popOverViewController, animated: true, completion:nil)
}

Text overlapping with custom button in TextView swift

Problem: Im trying to build a "comment" component to my app. I have managed to create a textview that expands as more text gets typed, and also created a button within the textview that will stay top right corner at all times. However when i write text, the text will go under the button and is not visible. What i want is that the text does not overlap the button, and instead just stays away from the button.
Question
How can i achieve this ?
This is my code:
class ClickedOnPostViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
var answersOf: Answer?
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var commentText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
commentText.translatesAutoresizingMaskIntoConstraints = false
[
commentText.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
commentText.leadingAnchor.constraint(equalTo: view.leadingAnchor),
commentText.trailingAnchor.constraint(equalTo: view.trailingAnchor),
commentText.heightAnchor.constraint(equalToConstant: 43)
].forEach{ $0.isActive = true }
commentText.addSubview(button)
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.topAnchor.constraint(equalTo: commentText.topAnchor).isActive = true
button.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
view.bringSubview(toFront: button)
commentText.delegate = self
commentText.isScrollEnabled = false
//Keyboard listeners
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
// qNotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
// Do any additional setup after loading the view.
}
let button: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .orange
button.setTitle("Click Me", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
#IBAction func refresh(_ sender: Any) {
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
commentText.resignFirstResponder()
return false
}
return true
}
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: view.frame.width, height: .infinity)
let estimatedSize = textView.sizeThatFits(size)
textView.constraints.forEach { (constraints) in
if constraints.firstAttribute == .height {
constraints.constant = estimatedSize.height
}
}
}
}
UPDATE issues with Sh_Khan current answer:
Before typing the last character that will overlap button
After typing the last character that will overlap button
You need
commentText.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(commentText)
view.addSubview(button)
NSLayoutConstraint.activate([
commentText.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
commentText.leadingAnchor.constraint(equalTo: view.leadingAnchor),
commentText.heightAnchor.constraint(equalToConstant: 43),
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 100),
button.topAnchor.constraint(equalTo: commentText.topAnchor),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
button.leadingAnchor.constraint(equalTo: commentText.trailingAnchor,constant:20)
])
commentText.delegate = self
commentText.isScrollEnabled = false
Don't mix RTL with LTR logic in your case using rightAnchor with trailingAnchor
If the UI of comment textfield is build in storyboard / xib , don't set constraints for it again
to have the width of textView = 80% , remove this
button.heightAnchor.constraint(equalToConstant: 50),
and add this
commentText.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier:0.8,constant:-20),

UIVisualEffectView creating unwanted shadow while presenting new view

In my custom presentation transition I've created a new view controller which will pre presented on top of the current active view controller (see screenshot). Somehow there's a shadow behind the blue view controller and I have no idea where it's coming from. Is there a way to stop getting that shadow?
The project is completely empty and has only 2 empty view controllers.
This is the code I'm using:
class ViewController: UIViewController {
let transitionDelegate = TransitionManager()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellowColor()
let button = UIButton(type: .System)
button.frame = CGRectMake(10, 10, 50, 50)
button.addTarget(self, action: "test:", forControlEvents: .TouchUpInside)
button.backgroundColor = UIColor.redColor()
view.addSubview(button)
}
func test(sender: UIButton) {
let destination = UIViewController()
destination.view.backgroundColor = .blueColor()
destination.transitioningDelegate = transitionDelegate
destination.modalPresentationStyle = .Custom
presentViewController(destination, animated: true, completion: nil)
}
}
The code for presenting the view:
class PresentingTransition: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.3
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let presented = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let container = transitionContext.containerView()!
let durations = transitionDuration(transitionContext)
presented.view.alpha = 0
container.addSubview(presented.view)
UIView.animateWithDuration(durations, animations: { presented.view.alpha = 1 }) { transitionContext.completeTransition($0) }
}
}
The code for handling the presenting view controller:
class PresentationController: UIPresentationController {
var background: UIView!
override init(presentedViewController: UIViewController, presentingViewController: UIViewController) {
super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
prepareBackground()
}
func prepareBackground() {
self.background = UIView(frame: presentingViewController.view.bounds)
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blur.frame = background.bounds
blur.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
background.addSubview(blur)
let tapRecognizer = UITapGestureRecognizer(target: self, action: "backgroundTapped:")
background.addGestureRecognizer(tapRecognizer)
}
func backgroundTapped(tapRecognizer: UITapGestureRecognizer) {
presentingViewController.dismissViewControllerAnimated(true, completion: nil)
}
override func presentationTransitionWillBegin() {
let container = containerView!
background.frame = container.bounds
background.alpha = 0.0
container.insertSubview(background, atIndex: 0)
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in self.background.alpha = 1.0 }, completion: nil)
}
override func dismissalTransitionWillBegin() {
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in self.background.alpha = 0.0 }, completion: nil)
}
override func frameOfPresentedViewInContainerView() -> CGRect {
return containerView!.bounds.insetBy(dx: 100, dy: 100)
}
override func containerViewWillLayoutSubviews() {
background.frame = containerView!.bounds
presentedView()!.frame = frameOfPresentedViewInContainerView()
}
}