scrolling not working inside childview - swift(iOS) - ios

I have added child view inside the containerview, containerview height will update as subview height changed.when Bzcardview display,it will hide containerview and when containerview display it will hide Bzcardview. Bazcardview and containerview both having same contraint.
func UpdateSubviewHeight() {
if segmentHeaderView.selectedSegmentIndex == 0
{
self.containerviewHeighContraint.constant = InformationTabView.InnerviewHeightContraint.constant + 50
}
else {
self.containerviewHeighContraint.constant = self.BzcardView.view.height
}
self.view.layoutIfNeeded()
}
private func addChildViewController(vc: UIViewController) {
self.Containerview.addSubview(vc.view)
if vc == FirstTabView
{
self.containerviewHeighContraint.constant = InformationTabView.InnerviewHeightContraint.constant
}
else {
self.containerviewHeighContraint.constant = vc.view.height
}
self.view.layoutIfNeeded()
activateRequiredConstraints(for: vc.view)
vc.didMove(toParent: self)
}
private func activateRequiredConstraints(for childView: UIView) {
childView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
childView.leadingAnchor.constraint(equalTo: Containerview.leadingAnchor, constant: 0),
childView.trailingAnchor.constraint(equalTo: Containerview.trailingAnchor, constant: 0),
childView.topAnchor.constraint(equalTo: Containerview.topAnchor, constant: 0),
childView.bottomAnchor.constraint(equalTo: Containerview.bottomAnchor, constant: 0),
childView.heightAnchor.constraint(equalTo: Containerview.heightAnchor, constant: 0)
])
}
When tab changed it just perform hide and show action.
if segmentedControl.selectedSegmentIndex == 0
{
self.InformationTabView.view.isHidden = false
self.ExposuresView.view.isHidden = true
self.Bzcardview_.isHidden = true
self.scrollview.isScrollEnabled = true
self.Bzcardview_.isHidden = true
self.Bzcardview_.isUserInteractionEnabled = false
}
else if segmentedControl.selectedSegmentIndex == 1
{
self.InformationTabView.view.isHidden = true
self.Bzcardview_.isHidden = false
self.Bzcardview_.isUserInteractionEnabled = true
self.ExposuresView.view.isHidden = true
self.scrollview.isScrollEnabled = false
}

override func layoutSubviews() {
super.layoutSubviews()
DispatchQueue.main.async {
if self.Bzcardview_.isHidden{
self.containerviewHeighContraint.constant = vc.view.height
self.containerviewHeighContraint.constant = self.BzcardView.view.height
}else{
self.containerviewHeighContraint.constant = InformationTabView.InnerviewHeightContraint.constant + 50
self.containerviewHeighContraint.constant = InformationTabView.InnerviewHeightContraint.constant
}
}
}

Related

Fold and unfold animation of UIImageView using constraints

As you can see in my code bellow, I'm trying to make a fold/unfold animation using constraints. Certainly the gray background has the fold/unfold animation but the image itself doesn't.
How can I get same fold/unfold effect of the image itself?
class ViewController2: UIViewController {
var folded: Bool = false
var imagen: UIImageView!
private var foldConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let imagen = UIImageView(contentMode: .scaleAspectFill, image: #imageLiteral(resourceName: "gpointbutton"))
imagen.translatesAutoresizingMaskIntoConstraints = false
imagen.backgroundColor = .gray
view.addSubview(imagen)
self.imagen = imagen
imagen.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imagen.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 0)
createAnimationButton()
}
private func createAnimationButton() {
let button = UIButton(title: "Animate", titleColor: .blue)
button.translatesAutoresizingMaskIntoConstraints = false
button.addAction(for: .touchUpInside) { [weak self] (_) in
guard let self = self else { return }
self.folded = !self.folded
if self.folded {
self.foldConstraint.isActive = true
UIView.animate(withDuration: 0.5) {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}
} else {
self.foldConstraint.isActive = false
UIView.animate(withDuration: 0.5) {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}
}
}
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
One thing to note here is that the width or height constraint is set to 0 (accurately also includes 0.1), and the same is hidden.
Then you need to set the height constraint to be greater than 0.1
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 0)
Replace with this, temporarily set to 1
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 1)
Hide it at the end of the animation
self.folded = !self.folded
if self.folded {
self.foldConstraint.isActive = true
UIView.animate(withDuration: 1, animations: {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}) { (completion) in
self.imagen.isHidden = true
}
} else {
self.imagen.isHidden = false
self.foldConstraint.isActive = false
UIView.animate(withDuration: 1, animations: {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
})
}
Update:
scaleAspectFill is not suitable for animation, it should be set to scaleAspectFit
let imagen = UIImageView(contentMode: .scaleAspectFit, image: #imageLiteral(resourceName: "gpointbutton"))

Updating layout anchors not working as expected

I have created a view with height 100 using NSLayout anchors. When I'm trying to update that on button click, it's not working.
I have tried below code, but it's not working.
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
let viewAnimate = UIView()
var isHidden = false
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(viewAnimate)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewAnimate.translatesAutoresizingMaskIntoConstraints = false
viewAnimate.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8).isActive = true
viewAnimate.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -8).isActive = true
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
viewAnimate.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewAnimate.backgroundColor = UIColor.red
}
#IBAction func show() {
if !isHidden {
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
}
UIView.animate(withDuration: 1) {
self.viewAnimate.layoutIfNeeded()
}
isHidden = !isHidden
}
}
View should change the height based on height constraint
Your current code creates conflicts as every line like viewAnimate.topAnchor.constraint(equalTo: adds a new constraint , create a var
var topCon:NSLayoutConstraint!
topCon = viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100)
topCon.isActive = true
#IBAction func show() {
if !isHidden {
topCon.constant = 200
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
topCon.constant = 100
}
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
isHidden = !isHidden
}

Animate constraints change UIViewController

I have "error" view, that is placed above navigation bar and hidden. When error occured, i want that view to smoothly show from top. I tried:
class AuthViewController: UIViewController {
let error: ErrorView = {
let error = ErrorView()
error.setup()
return error
}()
var topAnchor: NSLayoutConstraint!
var botAnchor: NSLayoutConstraint!
override func viewDidLoad() {
setupErrorView()
}
private func setupErrorView(){
view.addSubview(error)
botAnchor = error.bottomAnchor.constraint(equalTo: view.topAnchor)
botAnchor.isActive = true
topAnchor = error.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(Offsets.navigationAndStatusBarHeight))
topAnchor.isActive = false
error.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
error.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
func showError(_ text: String){
UIView.animate(withDuration: 2.0) {[weak self] in
guard let weakSelf = self else { return }
print("attempt to animate")
weakSelf.error.show(text)
weakSelf.botAnchor.isActive = false
weakSelf.topAnchor.isActive = true
weakSelf.view.setNeedsLayout()
}
}
}
class ErrorView: UIView {
private var label: UILabel = {
return LabelSL.regular()
}()
fileprivate func setup(){
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = Theme.Color.orange.value
addSubview(label)
}
fileprivate func show(_ text: String){
let sideOffset: CGFloat = 10
let verticalOffset: CGFloat = 10
label.text = text
label.topAnchor.constraint(equalTo: topAnchor, constant: verticalOffset).isActive = true
label.leftAnchor.constraint(equalTo: leftAnchor, constant: sideOffset).isActive = true
label.rightAnchor.constraint(equalTo: rightAnchor, constant: -sideOffset).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -verticalOffset).isActive = true
}
}
Animation should be done when func showError(_ text: String){ method called, but it's not. View just appear instantly.
You're trying to animate constraints in wrong way. You should set constraints outside of animation block and only layoutIfNeeded in animation:
func showError(_ text: String){
botAnchor.isActive = false
topAnchor.isActive = true
error.show(text)
UIView.animate(withDuration: 2.0) {
self.view.layoutIfNeeded()
}
}

Reusable ViewControllers in ScrollView Pass By Reference

I am attempting to create a UIScrollView with infinitely many horizontally scrolling pages. I plan to use instances of several reusable cached ViewControllers to fill the pages. To achieve this I am trying to create a similar effect to UIPageViewController by using a beforeViewController a currentViewController and an afterViewController to fill the the three positions as the user scrolls so there is always one viewController before and after the current one the user is looking at to scroll to.
I would like to keep setting these instances equal to varying viewControllers from my cache but as I scroll back and forth eventually I am left with an empty ViewController. I believe it is caused by the passed by reference nature of the ViewController and setting one equal to the other.
Below is the code I have created is there any way to improve the functionality. Thank you in advance for your help.
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
var previousOffset:CGFloat = 0
var viewControllers:[UIViewController] = [UIViewController](){
didSet{
print("viewControllers.count: \(viewControllers.count)")
}
}
var beforeViewController:UIViewController?
var currentViewController:UIViewController?
var nextViewController:UIViewController?
var scrollView:UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.backgroundColor = UIColor.lightGray
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
scrollView.delegate = self
//3 Pages
scrollView.contentSize = CGSize(width: 3 * self.view.bounds.width, height: self.view.bounds.height)
self.view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0),
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
])
currentViewController = getViewController()
addViewController(viewController: currentViewController!, index: 0) { (view) in
view.backgroundColor = UIColor.green
}
nextViewController = getViewController()
addViewController(viewController: nextViewController!, index: 1) { (view) in
view.backgroundColor = UIColor.purple
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let currentPage = scrollView.contentOffset.x/scrollView.bounds.width
print("current page: \(currentPage)")
if scrollView.contentOffset.x > previousOffset{
print("User scrolled Forward")
scrolledForwards(currentPage: Int(currentPage))
}else if scrollView.contentOffset.x < previousOffset{
print("User scrolled backwards")
scrolledBackwards(currentPage: Int(currentPage))
}
previousOffset = scrollView.contentOffset.x
}
func getViewController()->UIViewController{
let unusedViewControllers:[UIViewController] = self.viewControllers.filter({return $0.parent == nil})
if let unusedViewController = unusedViewControllers.first{
print("reusing viewController: \(viewControllers.count)")
print("reusing viewController: \(unusedViewController.description)")
return unusedViewController
}else{
let newViewController = UIViewController()
self.viewControllers.append(newViewController)
print("creating new viewController")
return newViewController
}
}
func addViewController(viewController:UIViewController,index:Int, completion: ((UIView)->Void)? = nil){
self.willMove(toParent: viewController)
self.addChild(viewController)
guard let view = viewController.view else{
removeViewController(viewController: viewController)
fatalError("view controller sent without a view")
}
self.scrollView.addSubview(view)
viewController.didMove(toParent: self)
let offset = self.view.bounds.width * CGFloat(index)
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0),
view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: offset),
view.heightAnchor.constraint(equalTo: scrollView.heightAnchor, constant: 0),
view.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0)
])
if let completion = completion{
completion(view)
}
}
func removeViewController(viewController:UIViewController?, completion: ((UIView)->Void)? = nil){
viewController?.willMove(toParent: nil)
viewController?.view.removeFromSuperview()
viewController?.removeFromParent()
if let completion = completion{
completion(view)
}
}
func scrolledForwards(currentPage:Int = 0){
removeViewController(viewController: beforeViewController)
let index = currentPage + 1
self.beforeViewController = self.currentViewController
self.currentViewController = self.nextViewController
print("index: \(index)")
if index > 2{
print("There is no more forwards")
return
}
self.nextViewController = getViewController()
if nextViewController?.parent == nil{
let index = currentPage + 1
self.addViewController(viewController: nextViewController!, index: index) { (view) in
view.backgroundColor = .magenta
}
}
}
func scrolledBackwards(currentPage:Int = 0){
let index = currentPage - 1
removeViewController(viewController: nextViewController)
nextViewController = currentViewController
currentViewController = beforeViewController
print("index: \(index)")
if index < 0{
print("There is no more backwards")
return
}
beforeViewController = getViewController()
currentViewController?.view.backgroundColor = UIColor.brown
if beforeViewController?.parent == nil{
self.addViewController(viewController: beforeViewController!, index: index) { (view) in
view.backgroundColor = .cyan
}
}
}
}

Why dont some of my anchor constraints work while others do for the same item?

I am trying to put anchor constraints on a textField contained within a subView. For the leading and top anchors the anchors work but for the bottom and trailing anchors they dont. Im not sure what it could be, I would like some space between the keyboard and the items in my subview as well as some space between the UITextField and the trailing anchor edge of the screen. Below is the code in question
Layout Code:
func setUpLayout(){
//myView
self.myView.translatesAutoresizingMaskIntoConstraints = false
self.myView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.myView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.myView.heightAnchor.constraint(equalToConstant: 98).isActive = true
self.myView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 4).isActive = true
//CollectionView
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 0).isActive = true
self.collectionView.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 0).isActive = true
self.collectionView.bottomAnchor.constraint(equalTo: self.myView.bottomAnchor, constant: 4).isActive = true
self.collectionView.topAnchor.constraint(equalTo: self.searchBar.bottomAnchor, constant: 4).isActive = true
//searchbar
self.searchBar.translatesAutoresizingMaskIntoConstraints = false
self.searchBar.topAnchor.constraint(equalTo: self.myView.topAnchor, constant: 0).isActive = true
self.searchBar.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 4).isActive = true
self.searchBar.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 4).isActive = true
self.searchBar.heightAnchor.constraint(equalToConstant: 45).isActive = true
searchBar.backgroundColor = .white
searchBar.layer.borderWidth = 2
searchBar.layer.borderColor = UIColor.black.cgColor
//PictureView
self.PictureView.translatesAutoresizingMaskIntoConstraints = false
self.PictureView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
self.PictureView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
self.PictureView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.PictureView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.PictureView.backgroundColor = .green
}
Code in its entirety
import UIKit
class SearchCollectionViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout, UITextFieldDelegate, UICollectionViewDataSource,UIGestureRecognizerDelegate {
var myView: UIView!
var searchBar: UITextField!
var collectionView: UICollectionView!
var PictureView: UIView!
var genericArray:[String] = ["A","B","C","D","E","F","G","Ab","Abc","za"]
var currentGenericArray:[String] = [String]()
var tagsSelected:[String] = [String]()
let keyboardSlider = KeyboardSlider()
override func viewDidLoad() {
super.viewDidLoad()
keyboardSlider.subscribeToKeyboardNotifications(view: view)
myView = UIView(frame: CGRect(x: 0, y: self.view.frame.height, width: self.view.frame.width, height: self.view.frame.height))
searchBar = UITextField(frame: CGRect(x: 0, y: 0, width: self.myView.frame.width, height: self.myView.frame.height))
PictureView = UIView(frame: self.view.frame)
self.view.addSubview(PictureView)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: (UIScreen.main.bounds.width-1)/2, height: (UIScreen.main.bounds.width-1)/2)
layout.scrollDirection = .horizontal
self.collectionView = UICollectionView(frame: CGRect(x: 0, y: self.myView.frame.height, width: self.myView.frame.width, height: 100), collectionViewLayout: layout)
collectionView.backgroundColor = .clear
collectionView.contentInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
myView.backgroundColor = .clear
collectionView.delegate = self
collectionView.dataSource = self
self.myView.addSubview(collectionView)
self.view.addSubview(myView)
collectionView.register(CollectionCell.self, forCellWithReuseIdentifier: "collectionViewCell")
currentGenericArray = genericArray
searchBar.delegate = self
searchBar.autocorrectionType = .no
searchBar.keyboardType = .default
searchBar.addTarget(self, action: #selector(SearchCollectionViewController.textFieldDidChange), for: .editingChanged)
self.myView.addSubview(searchBar)
let viewTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(gestureRecognizer:)))
viewTapGestureRecognizer.cancelsTouchesInView = false
self.PictureView.addGestureRecognizer(viewTapGestureRecognizer)
self.PictureView.backgroundColor = .purple
self.view.backgroundColor = .green
self.searchBar.becomeFirstResponder()
self.collectionView.allowsSelection = true
setUpLayout()
}
func setUpLayout(){
//myView
self.myView.translatesAutoresizingMaskIntoConstraints = false
self.myView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.myView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.myView.heightAnchor.constraint(equalToConstant: 98).isActive = true
self.myView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 4).isActive = true
//CollectionView
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 0).isActive = true
self.collectionView.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 0).isActive = true
self.collectionView.bottomAnchor.constraint(equalTo: self.myView.bottomAnchor, constant: 4).isActive = true
self.collectionView.topAnchor.constraint(equalTo: self.searchBar.bottomAnchor, constant: 4).isActive = true
//searchbar
self.searchBar.translatesAutoresizingMaskIntoConstraints = false
self.searchBar.topAnchor.constraint(equalTo: self.myView.topAnchor, constant: 0).isActive = true
self.searchBar.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 4).isActive = true
self.searchBar.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 4).isActive = true
self.searchBar.heightAnchor.constraint(equalToConstant: 45).isActive = true
searchBar.backgroundColor = .white
searchBar.layer.borderWidth = 2
searchBar.layer.borderColor = UIColor.black.cgColor
//PictureView
self.PictureView.translatesAutoresizingMaskIntoConstraints = false
self.PictureView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
self.PictureView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
self.PictureView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.PictureView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.PictureView.backgroundColor = .green
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
keyboardSlider.unsubscribeFromKeyboardNotifications()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
collectionView.reloadData()
return true
}
/// Helper to dismiss keyboard
#objc func didStopEditing() {
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.animate(withDuration: 0.2) {
self.view.frame.origin.y = 0
}
}
#objc func textFieldDidChange(){
guard(!(searchBar.text?.isEmpty)!) else{
currentGenericArray = genericArray
collectionView.reloadData()
return
}
currentGenericArray = genericArray.filter({letter -> Bool in
if searchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: searchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(searchBar.text!.lowercased())
})
if currentGenericArray.isEmpty{
print("text being inserted \(searchBar.text!)")
currentGenericArray.append(searchBar.text!)
}
collectionView.reloadData()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if (touch.view?.isDescendant(of: self.collectionView))!{
return false
}
return true
}
var keyboardIsOpen:Bool = false
#objc func viewTapped(gestureRecognizer:UIGestureRecognizer){
if keyboardIsOpen{
myView.isHidden = true
keyboardIsOpen = !keyboardIsOpen
searchBar.resignFirstResponder()
}
else{
myView.isHidden = false
keyboardIsOpen = !keyboardIsOpen
searchBar.becomeFirstResponder()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0{
return tagsSelected.count
}
else {
return currentGenericArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionCell
if indexPath.section == 0{
cell.collectionLabel.text = tagsSelected[indexPath.item]
cell.backgroundColor = .blue
cell.collectionLabel.textColor = .white
}
else if indexPath.section == 1{
cell.backgroundColor = .white
cell.collectionLabel.textColor = UIColor.black
cell.collectionLabel.text = currentGenericArray[indexPath.row]
}
cell.layer.masksToBounds = true
cell.layer.cornerRadius = cell.bounds.width/20
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 6
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 1{\
if(tagsSelected.contains(currentGenericArray[indexPath.item])){
}
tagsSelected.append(currentGenericArray[indexPath.item])
for i in 0...genericArray.count-1{
if(currentGenericArray[indexPath.item] == genericArray[i]){
genericArray.remove(at: i)
break
}
}
currentGenericArray.remove(at: indexPath.item)
searchBar.text = ""
collectionView.reloadData()
if collectionView.numberOfItems(inSection: 1)>0{
collectionView.scrollToItem(at: IndexPath(item: 0, section: 1), at: .right, animated: true)
}
}
else if indexPath.section == 0{
currentGenericArray.append(tagsSelected[indexPath.item])
tagsSelected.remove(at: indexPath.item)
collectionView.reloadData()
}
}
var offsetY:CGFloat = 0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(SearchCollectionViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
#objc func keyboardFrameChangeNotification(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect
let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0
let animationCurveRawValue = (userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int) ?? Int(UIViewAnimationOptions.curveEaseInOut.rawValue)
let animationCurve = UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue))
if let _ = endFrame, endFrame!.intersects(self.myView.frame) {
self.offsetY = self.myView.frame.maxY - endFrame!.minY
} else {
if self.offsetY != 0 {
UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
self.myView.frame.origin.y = self.myView.frame.origin.y + self.offsetY
self.offsetY = 0
}, completion: nil)
}
}
}
}
}
class CollectionCell:UICollectionViewCell{
var collectionLabel: UILabel!
var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
collectionLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height))
self.addSubview(collectionLabel)
collectionLabel.textAlignment = .center
collectionLabel.translatesAutoresizingMaskIntoConstraints = false
collectionLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
collectionLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
collectionLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
collectionLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension UIView {
func currentFirstResponder() -> UIResponder? {
if self.isFirstResponder {
return self
}
for view in self.subviews {
if let responder = view.currentFirstResponder() {
return responder
}
}
return nil
}
}
extension Notification.Name{
static let showKeyboard = Notification.Name("showKeyboard")
}
class KeyboardSlider: NSObject {
// variables to hold and process information from the view using this class
weak var view: UIView?
#objc func keyboardWillShow(notification: NSNotification) {
// method to move keyboard up
view?.frame.origin.y = 0 - getKeyboardHeight(notification as Notification)
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
// get exact height of keyboard on all devices and convert to float value to return for use
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications(view: UIView) {
// assigning view to class' counterpart
self.view = view
// when UIKeyboardWillShow do keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
}
}
For trailing and bottom anchor constraints, it is common that you have to use negative numbers for the constants to achieve what you want.
For example:
searchBar.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 4).isActive = true
Becomes:
searchBar.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: -4).isActive = true

Resources