Reusable ViewControllers in ScrollView Pass By Reference - ios

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
}
}
}
}

Related

Transparency on a UITableView causes navigation animation to look bad

I'm using a UITableView with a Navigation Controller and I have made the former partially transparent, which looks great.
The problem I am running into is that when I press a button, the transition animation (to change to another view) looks odd because the old view that is sliding behind the new one is visible for a time.
I have tried things like temporarily shutting off transparency (either suddenly, or gradually), and while it looks a little better, overall the experience still isn't great.
I guess it might be possible to do a custom animation, but this seems like a bad idea since it will likely look different than the built-in OS animation. Actually, even with a custom animation I am not sure how I would do it since I think I would run into the same issue.
Does anyone have any ideas how I can make things look cleaner?
UPDATE: adding more detail based on questions asked in the comments
The UI is a pretty complex set of pieces but I'll try to describe the relevant parts here.
There is a UISplitViewController [A], and I have created a UIVisualEffectView (with UIBlurEffect) that is attached as a subview of A's parent. My menu consists of a UINavigationController [B], and a UITableViewController [C] that is the top level of the menu. [B] is added as a subview as the content view of the blur effect view.
Two other UITableViewControllers [D] and [E] are transitioned to when button [1] or [2] are pressed on [C].
There are a few other view controllers that are subviews of [A] (or [A]'s parent) that are showing through, blurred, but that is the design and there is no issue there.
The problem is for the transition animations from [C]->[D], [D]->[C] (via back button), [C]->[E], or [E]->[C], you can see the controller that is moving away behind the controller that is coming in. So if you do [C]->[D] (via pressing button [1] on [C]) then you will see [C] going behind [D] as it slides in, and [C] eventually disappears.
The actual showing of [D] or [E] is done via a line of code like this (inside the custom class of [C])
self.navigationController?.show(myVC, sender: self)
where navigationController is [B] and myVC is [D].
The transition back to [C] is done via popViewController().
OK - trying to (minimally) emulate your setup description...
View controller with an image view filling the entire view
Navigation controller added as a child VC
Two VCs for the nav controller...
both with transparent background
"Page 1" pushes to "Page 2"
So I assume you mean you have a current "push/pop" transition that looks like this with simulator Debug -> Slow Animations to exaggerate the effect (these are kinda "heavy" gifs, so open them in a new browser tab if the animation isn't running):
And your goal is something close or similar to this:
You will likely need to use a custom transition.
I was able to get those results using the code from this article unedited: Simple, custom navigation transitions -- note: this is not mine - just found it from quick searching.
Here's the code for the full example -- everything is done via code, no #IBOutlet or #IBAction connections needed. Just assign a new view controller's custom class as NavSubVC :
class NavSubVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
guard let img = UIImage(named: "navBKG") else {
DispatchQueue.main.async {
let a = UIAlertController(title: "Alert", message: "Could not load \"navBKG\" image", preferredStyle: .alert)
self.present(a, animated: true, completion: nil)
}
return
}
let imgView = UIImageView(image: img)
imgView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imgView)
let rvc = Page1VC()
let navC = UINavigationController(rootViewController: rvc)
self.addChild(navC)
guard let navView = navC.view else { return }
view.addSubview(navView)
navC.didMove(toParent: self)
navView.translatesAutoresizingMaskIntoConstraints = false
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
imgView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
navView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
navView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
navView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
navView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
])
// let's have a gray nav bar always showing
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithTransparentBackground()
navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
navigationBarAppearance.backgroundColor = .systemGray
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
// let's add a border to the navigation controller view
// so we can see its frame (since the controllers have clear backgrounds)
navView.layer.borderWidth = 2
navView.layer.borderColor = UIColor.yellow.cgColor
// un-comment this line to see the custom transition
//navC.addCustomTransitioning()
}
}
class PageBaseVC: UIViewController {
var labels: [UILabel] = []
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
for i in 1...6 {
let v = UILabel()
v.text = "\(i)"
v.textAlignment = .center
v.textColor = .white
v.translatesAutoresizingMaskIntoConstraints = false
v.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
v.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
labels.append(v)
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
labels[0].topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
labels[0].leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
labels[1].centerYAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
labels[1].leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
labels[2].bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
labels[2].leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
labels[3].topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
labels[3].trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
labels[4].centerYAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
labels[4].trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
labels[5].bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
labels[5].trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
])
}
}
class Page1VC: PageBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Page 1"
labels.forEach { v in
v.backgroundColor = .systemBlue
}
let b = UIButton()
b.backgroundColor = .systemGreen
b.setTitle("Push to Page 2", for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.lightGray, for: .highlighted)
b.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(b)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
b.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
b.centerXAnchor.constraint(equalTo: g.centerXAnchor),
b.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.75),
b.heightAnchor.constraint(equalToConstant: 60.0),
])
b.addTarget(self, action: #selector(doPush(_:)), for: .touchUpInside)
}
#objc func doPush(_ sender: Any?) {
let vc = Page2VC()
self.navigationController?.pushViewController(vc, animated: true)
}
}
class Page2VC: PageBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Page 2"
labels.forEach { v in
v.backgroundColor = .systemRed
}
}
}
// Custom Navigation Transition
// from: https://ordinarycoding.com/articles/simple-custom-uinavigationcontroller-transitions/
final class TransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
// 1
let presenting: Bool
// 2
init(presenting: Bool) {
self.presenting = presenting
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
// 3
return TimeInterval(UINavigationController.hideShowBarDuration)
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// 4
guard let fromView = transitionContext.view(forKey: .from) else { return }
guard let toView = transitionContext.view(forKey: .to) else { return }
// 5
let duration = transitionDuration(using: transitionContext)
// 6
let container = transitionContext.containerView
if presenting {
container.addSubview(toView)
} else {
container.insertSubview(toView, belowSubview: fromView)
}
// 7
let toViewFrame = toView.frame
toView.frame = CGRect(x: presenting ? toView.frame.width : -toView.frame.width, y: toView.frame.origin.y, width: toView.frame.width, height: toView.frame.height)
let animations = {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5) {
toView.alpha = 1
if self.presenting {
fromView.alpha = 0
}
}
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1) {
toView.frame = toViewFrame
fromView.frame = CGRect(x: self.presenting ? -fromView.frame.width : fromView.frame.width, y: fromView.frame.origin.y, width: fromView.frame.width, height: fromView.frame.height)
if !self.presenting {
fromView.alpha = 0
}
}
}
UIView.animateKeyframes(withDuration: duration,
delay: 0,
options: .calculationModeCubic,
animations: animations,
completion: { finished in
// 8
container.addSubview(toView)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
final class TransitionCoordinator: NSObject, UINavigationControllerDelegate {
// 1
var interactionController: UIPercentDrivenInteractiveTransition?
// 2
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push:
return TransitionAnimator(presenting: true)
case .pop:
return TransitionAnimator(presenting: false)
default:
return nil
}
}
// 3
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}
}
extension UINavigationController {
// 1
static private var coordinatorHelperKey = "UINavigationController.TransitionCoordinatorHelper"
// 2
var transitionCoordinatorHelper: TransitionCoordinator? {
return objc_getAssociatedObject(self, &UINavigationController.coordinatorHelperKey) as? TransitionCoordinator
}
func addCustomTransitioning() {
// 3
var object = objc_getAssociatedObject(self, &UINavigationController.coordinatorHelperKey)
guard object == nil else {
return
}
object = TransitionCoordinator()
let nonatomic = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &UINavigationController.coordinatorHelperKey, object, nonatomic)
// 4
delegate = object as? TransitionCoordinator
// 5
let edgeSwipeGestureRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
edgeSwipeGestureRecognizer.edges = .left
view.addGestureRecognizer(edgeSwipeGestureRecognizer)
}
// 6
#objc func handleSwipe(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) {
guard let gestureRecognizerView = gestureRecognizer.view else {
transitionCoordinatorHelper?.interactionController = nil
return
}
let percent = gestureRecognizer.translation(in: gestureRecognizerView).x / gestureRecognizerView.bounds.size.width
if gestureRecognizer.state == .began {
transitionCoordinatorHelper?.interactionController = UIPercentDrivenInteractiveTransition()
popViewController(animated: true)
} else if gestureRecognizer.state == .changed {
transitionCoordinatorHelper?.interactionController?.update(percent)
} else if gestureRecognizer.state == .ended {
if percent > 0.5 && gestureRecognizer.state != .cancelled {
transitionCoordinatorHelper?.interactionController?.finish()
} else {
transitionCoordinatorHelper?.interactionController?.cancel()
}
transitionCoordinatorHelper?.interactionController = nil
}
}
}

scrolling not working inside childview - swift(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
}
}
}

Custom Views in today widget stack on each other instead of one under the other

I'm having an issue i can't figure out.
I need to build a widget and i have two custom views (Right now using the same custom view)
The issue is that for some reason they stack on each other (I can see that in the first print i have on sub view and in the second one i have two and see only the second subview constraint to the top of the containing view)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded
guard let currentView = Bundle.main.loadNibNamed("CurrentView", owner: self, options: nil)?.first as? CurrentView else { return }
currentView.frame = self.view.frame
self.view.addSubview(currentView)
currentView.forcastImage.image = #imageLiteral(resourceName: "13")
currentView.translatesAutoresizingMaskIntoConstraints = false
currentView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
currentView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
currentView.heightAnchor.constraint(equalToConstant: 110).isActive = true
print(self.view.subviews.count)
if let hourlyView = Bundle.main.loadNibNamed("CurrentView", owner: self, options: nil)?.first as? CurrentView {
hourlyView.frame = self.view.frame
self.view.addSubview(hourlyView)
hourlyView.forcastImage.image = #imageLiteral(resourceName: "6")
hourlyView.translatesAutoresizingMaskIntoConstraints = false
hourlyView.topAnchor.constraint(equalTo: currentView.bottomAnchor, constant: 150).isActive = true
hourlyView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
hourlyView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
hourlyView.heightAnchor.constraint(equalToConstant: 110).isActive = true
}
print(self.view.subviews.count)
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
if activeDisplayMode == .expanded {
preferredContentSize = CGSize(width: 0, height: 250)
} else {
preferredContentSize = maxSize
}
}
Any idea why is that?
You may need
currentView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true

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()
}
}

Add imageview to a scrollview page control

My code below is a basic scrollview using page control which uses basic colors on each screen. I want to display an imageview over the uiview which displays the basic colors. I want to call var pics from the asset file in Xcode.
var colors:[UIColor] = [UIColor.red, UIColor.blue, UIColor.green, UIColor.yellow]
var pics = ["a","b","c","d"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
for index in 0..<4 {
frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
frame.size = self.scrollView.frame.size
let subView = UIView(frame: frame)
subView.backgroundColor = colors[index]
self.scrollView .addSubview(subView)
}
func configurePageControl() {
self.pageControl.numberOfPages = colors.count
}
Here I suppose that you have 4 images attached in your project named 0,1,2,3 png , try this
import UIKit
class ViewController: UIViewController , UIScrollViewDelegate {
let scrollView = UIScrollView()
let pageCon = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
let viewsCount = 4
var prevView = self.view!
scrollView.delegate = self
scrollView.isPagingEnabled = true
pageCon.numberOfPages = viewsCount
pageCon.currentPage = 0
pageCon.tintColor = .green
pageCon.currentPageIndicatorTintColor = .orange
pageCon.backgroundColor = .blue
pageCon.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
view.insertSubview(pageCon, aboveSubview: scrollView)
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant:20),
scrollView.heightAnchor.constraint(equalToConstant: 400),
pageCon.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pageCon.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant:-20),
])
for i in 0..<viewsCount {
let imageV = UIImageView()
imageV.image = UIImage(named: "\(i).png")
imageV.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(imageV)
if prevView == self.view {
imageV.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
}
else {
imageV.leadingAnchor.constraint(equalTo: prevView.trailingAnchor).isActive = true
}
NSLayoutConstraint.activate([
imageV.topAnchor.constraint(equalTo: scrollView.topAnchor),
imageV.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
imageV.widthAnchor.constraint(equalToConstant: self.view.frame.width),
imageV.heightAnchor.constraint(equalToConstant: 400)
])
if i == viewsCount - 1 {
imageV.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
}
prevView = imageV
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageCon.currentPage = Int(scrollView.contentOffset.x / self.view.frame.width)
}
}

Resources