UISegmented Control Inner view should call API after view has been shown - ios

I have a 'UISegmentedControl' which is programatically added in the view controller named SegmentControllForRides. (i added UISegmentedControl programatically because segment items will be changed some time 2 or 3.UISegmentedControl have three items. items names are ["Request","Upcoming", "Past"]. Each segment will show a viewController.view and hide the others. Each view controller have an API call. My problem is when first the SegmentControllForRides shows it calls the all three (child) viewcontroller API's( i think because views are hidden but loaded in controller thats why API calls goes to server). Each view controller has a UITableView and in UITableViewCell there is a UICollectionView. I tried to call (again) API when view is shown, in that case API call goes but UICollectionView got messed. It shows data of the other cell. For this i tried to
DispatchQueue.main.async {
reloadCollectionView()
}
but this does not help.
I want to call API when child view is shown (not on the SegmentControllForRides load). It is also necessary because first controller API response have the effect of the second controller API call. So Kindly guide me how to call API after the view shown.
I will try provide any further detail.
Here is my class.
import UIKit
class SegmentControllForRides: UIViewController {
// MARK: - Variables
var controller: UIViewController!
var requestedRideViewController: RequestedRideViewController!
var upcomingRideViewController: UpcomingRideViewController!
var myRideViewControllerUpcoming: UIViewController!
var myRideViewControllerPast: UIViewController!
var segmentControll: UISegmentedControl!
var sideMenuOpen = false
// MARK: - Outlets
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
configure()
}
override func viewWillDisappear(_ animated: Bool) {
if sideMenuOpen {
openCloseSideMenu()
}
}
// MARK: - Actions & Events
#IBAction func segmentControllChanged(_ sender: UISegmentedControl) {
if UserDefaults.standard.bool(forKey: User.isAppUsingAsPassenger) {
switch segmentControll.selectedSegmentIndex {
case 0:
print("segment 1")
myRideViewControllerUpcoming.view.isHidden = false
myRideViewControllerUpcoming.didMove(toParent: self)
myRideViewControllerPast.view.isHidden = true
case 1:
print("segment 2")
myRideViewControllerUpcoming.view.isHidden = true
myRideViewControllerPast.view.isHidden = false
myRideViewControllerPast.didMove(toParent: self)
default:
break
}
} else {
switch segmentControll.selectedSegmentIndex {
case 0:
print("segment 1")
requestedRideViewController.view.isHidden = false
requestedRideViewController.didMove(toParent: self)
upcomingRideViewController.view.isHidden = true
myRideViewControllerPast.view.isHidden = true
case 1:
print("segment 2")
requestedRideViewController.view.isHidden = true
upcomingRideViewController.view.isHidden = false
// upcomingRideViewController.getUpcomingRide()
upcomingRideViewController.didMove(toParent: self)
myRideViewControllerPast.view.isHidden = true
// let myClass : UpcomingRideViewController = self.children[1] as! UpcomingRideViewController
// myClass.myRideTableView.reloadData()
// myClass.getUpcomingRide()
// myClass.viewWillAppear(false)
case 2:
print("Segment 3")
requestedRideViewController.view.isHidden = true
upcomingRideViewController.view.isHidden = true
myRideViewControllerPast.view.isHidden = false
myRideViewControllerPast.didMove(toParent: self)
default:
break
}
}
}
#IBAction func sideMenuClicked(_ sender: UIBarButtonItem) {
openCloseSideMenu()
}
#objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
if gesture.direction == .right {
print("Swipe Right")
if !sideMenuOpen {
openCloseSideMenu()
}
} else if gesture.direction == .left {
print("Swipe Left")
if sideMenuOpen {
openCloseSideMenu()
}
}
}
// MARK: - Helper Methods
private func configure() {
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))
swipeLeft.direction = .left
self.view.addGestureRecognizer(swipeLeft)
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))
swipeRight.direction = .right
self.view.addGestureRecognizer(swipeRight)
controller = storyboard!.instantiateViewController(withIdentifier: "SideMenuViewController")
if UserDefaults.standard.bool(forKey: User.isAppUsingAsPassenger) {
let segmentItems = ["Upcoming", "Past"]
segmentControll = UISegmentedControl(items: segmentItems)
segmentControll.frame = CGRect(x:0 ,y: 0, width: view.frame.width, height: 30)
segmentControll.addTarget(self, action: #selector(segmentControllChanged), for: .valueChanged)
segmentControll.selectedSegmentIndex = 0
segmentControll.backgroundColor = .rideelyGray
if #available(iOS 13.0, *) {
segmentControll.selectedSegmentTintColor = .rideelyYellow
} else {
// Fallback on earlier versions
}
view.addSubview(segmentControll)
myRideViewControllerUpcoming = storyboard!.instantiateViewController(withIdentifier: "MyRideViewController")
addChild(myRideViewControllerUpcoming)
myRideViewControllerUpcoming.view.frame = CGRect(x: 0, y: 30, width: view.frame.width, height: view.frame.height - 30) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(myRideViewControllerUpcoming.view)
myRideViewControllerUpcoming.didMove(toParent: self)
myRideViewControllerUpcoming.view.frame = CGRect(x: 0 - self.view.frame.width, y: 30, width: view.frame.width, height: view.frame.height - 30)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.myRideViewControllerUpcoming.view.frame = CGRect(x: 0, y: 30, width: self.view.frame.width, height: self.view.frame.height - 30)
}, completion:nil)
myRideViewControllerUpcoming.view.isHidden = false
myRideViewControllerPast = storyboard!.instantiateViewController(withIdentifier: "MyRideViewController")
addChild(myRideViewControllerPast)
myRideViewControllerPast.view.frame = CGRect(x: 0, y: 30, width: view.frame.width, height: view.frame.height - 30) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(myRideViewControllerPast.view)
myRideViewControllerPast.didMove(toParent: self)
myRideViewControllerPast.view.frame = CGRect(x: 0 - self.view.frame.width, y: 30, width: view.frame.width, height: view.frame.height - 30)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.myRideViewControllerPast.view.frame = CGRect(x: 0, y: 30, width: self.view.frame.width, height: self.view.frame.height - 30)
}, completion:nil)
myRideViewControllerPast.view.isHidden = true
} else {
let segmentItems = ["Request","Upcoming", "Past"]
segmentControll = UISegmentedControl(items: segmentItems)
segmentControll.frame = CGRect(x:0 ,y: 0, width: view.frame.width, height: 30)
segmentControll.addTarget(self, action: #selector(segmentControllChanged), for: .valueChanged)
segmentControll.selectedSegmentIndex = 0
segmentControll.backgroundColor = .rideelyGray
if #available(iOS 13.0, *) {
segmentControll.selectedSegmentTintColor = .rideelyYellow
} else {
// Fallback on earlier versions
}
view.addSubview(segmentControll)
requestedRideViewController = (storyboard!.instantiateViewController(withIdentifier: "RequestedRideViewController") as! RequestedRideViewController)
addChild(requestedRideViewController)
requestedRideViewController.view.frame = CGRect(x: 0, y: 30, width: view.frame.width, height: view.frame.height) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(requestedRideViewController.view)
requestedRideViewController.didMove(toParent: self)
requestedRideViewController.view.frame = CGRect(x: 0 - self.view.frame.width, y: 30, width: view.frame.width, height: view.frame.height)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.requestedRideViewController.view.frame = CGRect(x: 0, y: 30, width: self.view.frame.width, height: self.view.frame.height - 30)
}, completion:nil)
requestedRideViewController.view.isHidden = false
upcomingRideViewController = (storyboard!.instantiateViewController(withIdentifier: "UpcomingRideViewController") as! UpcomingRideViewController)
addChild(upcomingRideViewController)
upcomingRideViewController.view.frame = CGRect(x: 0, y: 30, width: view.frame.width, height: view.frame.height - 30) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(upcomingRideViewController.view)
upcomingRideViewController.didMove(toParent: self)
upcomingRideViewController.view.frame = CGRect(x: 0 - self.view.frame.width, y: 30, width: view.frame.width, height: view.frame.height)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.upcomingRideViewController.view.frame = CGRect(x: 0, y: 30, width: self.view.frame.width, height: self.view.frame.height - 30)
}, completion:nil)
upcomingRideViewController.view.isHidden = true
myRideViewControllerPast = storyboard!.instantiateViewController(withIdentifier: "MyRideViewController")
addChild(myRideViewControllerPast)
myRideViewControllerPast.view.frame = CGRect(x: 0, y: 30, width: view.frame.width, height: view.frame.height - 30) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(myRideViewControllerPast.view)
myRideViewControllerPast.didMove(toParent: self)
myRideViewControllerPast.view.frame = CGRect(x: 0 - self.view.frame.width, y: 30, width: view.frame.width, height: view.frame.height - 30)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.myRideViewControllerPast.view.frame = CGRect(x: 0, y: 30, width: self.view.frame.width, height: self.view.frame.height - 30)
}, completion:nil)
myRideViewControllerPast.view.isHidden = true
}
}
private func openCloseSideMenu() {
if sideMenuOpen {
sideMenuOpen = false
controller.removeFromParent()
controller.view.removeFromSuperview()
} else {
sideMenuOpen = true
addChild(controller)
controller.view.frame = CGRect(x: 0, y: 0, width: view.frame.width * 0.8, height: view.frame.height) // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
view.addSubview(controller.view)
controller.didMove(toParent: self)
controller.view.frame = CGRect(x: 0 - self.view.frame.width, y: 0, width: view.frame.width * 0.8, height: view.frame.height)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.controller.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width * 0.8, height: self.view.frame.height)
}, completion:nil)
}
}
}

You need to configure your api calls in segmentControllChanged, there are many ways you can achieve this . One way that will best suit is
You can use flag for each controller to check when the segment is scrolled then only you hit the api explicitly otherwise don't hit it.
1 example for this
CODE -
switch segmentControll.selectedSegmentIndex {
case 1:
print("segment 1")
myRideViewControllerUpcoming.isVisible = true
// call your myRideViewControllerUpcoming api method here
}

Related

Swift changing button position doesn't work

I'm trying to change UIButton but it doesn't work. My code is like this.
#IBAction func addButtonTapped(_ sender: Any) {
print(defaultAddButton.center)
let buttonPos = CGPoint(x: defaultAddButton.frame.midX, y: defaultAddButton.frame.midY)
let width = self.view.bounds.width
let height: CGFloat = 100
let uiView = SubCollecionView(frame: CGRect(x: buttonPos.x - width / 2, y: buttonPos.y - height / 2, width: width, height: height))
self.view.addSubview(uiView)
self.defaultAddButton.center.y += 200
}
SubCollectionView is my custom view. Above code works without self.view.addSubview(uiView) line. I can't understand why it doesn't work. Thank you in advance!
do not fully understand your problem, but can suggest that you have a problem with animation, with this line of code:
self.defaultAddButton.center.y += 200
The above code doesn't work because you need to update the view to update its layout immediately, use this method to update view "layoutIfNeeded()"
Example of code:
class VC: UIViewController {
lazy var buttonAction: UIButton = {
let view = UIButton()
view.translatesAutoresizingMaskIntoConstraints = false
view.setTitle("Action", for: .normal)
view.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
view.backgroundColor = .blue
view.addTarget(self, action: #selector(action(_:)), for: .touchUpInside)
return view
}()
#objc func action(_ sender: UIButton) {
print("action")
print(buttonAction.center)
let buttonPos = CGPoint(x: buttonAction.frame.midX, y: buttonAction.frame.midY)
let width = self.view.bounds.width
let height: CGFloat = 100
let uiView = MediaPlayer(frame: CGRect(x: buttonPos.x - width / 2, y: buttonPos.y - height / 2, width: width, height: height))
self.view.addSubview(uiView)
self.buttonAction.center.y += 200
UIView.animate(withDuration: 1.0) {
self.view.layoutIfNeeded()
}
}
func setupButton() {
self.view.addSubview(buttonAction)
buttonAction.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
buttonAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -70).isActive = true
buttonAction.heightAnchor.constraint(equalToConstant: 50).isActive = true
buttonAction.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .gray
setupButton()
}
}
You can try setting changes/frame in view over
viewDidLayoutSubviews
You should call layoutIfNeeded() after this.
#IBAction func addButtonTapped(_ sender: Any) {
print(defaultAddButton.center)
let buttonPos = CGPoint(x: defaultAddButton.frame.midX, y: defaultAddButton.frame.midY)
let width = self.view.bounds.width
let height: CGFloat = 100
let uiView = SubCollecionView(frame: CGRect(x: buttonPos.x - width / 2, y: buttonPos.y - height / 2, width: width, height: height))
self.view.addSubview(uiView)
self.defaultAddButton.center.y += 200
self.view.layoutIfNeeded()
}
If this doesn't work. Please try to call UI modification code inside the main queue.
#IBAction func addButtonTapped(_ sender: Any) {
print(defaultAddButton.center)
DispatchQueue.main.async {
let buttonPos = CGPoint(x: defaultAddButton.frame.midX, y: defaultAddButton.frame.midY)
let width = self.view.bounds.width
let height: CGFloat = 100
let uiView = SubCollecionView(frame: CGRect(x: buttonPos.x - width / 2, y: buttonPos.y - height / 2, width: width, height: height))
self.view.addSubview(uiView)
self.defaultAddButton.center.y += 200
self.view.layoutIfNeeded()
}
}

Swift - Problem with scrollview (Need to print when dragging down)

I am testing the swiping controller/gesture from Jake Spracher with his SnapchatSwipeView (https://github.com/jakespracher/Snapchat-Swipe-View )
I have setup a topVC,leftVC,rightVC and middleVC(the main VC).
I manage to capture when the user swipe from the center to the rightVC or to the left VC, with this :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if delegate != nil && !delegate!.outerScrollViewShouldScroll() && !directionLockDisabled {
let newOffset = CGPoint(x: self.initialContentOffset.x, y: self.initialContentOffset.y)
self.scrollView!.setContentOffset(newOffset, animated: false)
}
if (scrollView.contentOffset) == CGPoint(x: 750.0, y: 0.0) {
print ("TEST OK LEFT!")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadRinkbox"), object: nil)
}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 0.0) {
print ("TEST OK LEFT!")
}
if (scrollView.contentOffset) == CGPoint(x: -750.0, y: 0.0) {
print ("TEST OK RIGHT")
}
if (scrollView.contentOffset) == CGPoint(x: 375, y: 0.0) {
print ("TEST OK!")
}
}
But I cannot manage to capture when the user swipe from the centerVC to the topVC, and from topVC to centerVC. I have tried a lot of things but I didn't manage to do it.
My code is for swift 4.
For clarity, I put here the two full swift files. Many, million thanks for those that can help me !
ContainerViewController.swift
//
// ContainerViewController.swift
// SnapchatSwipeView
//
// Created by Jake Spracher on 8/9/15.
// Copyright (c) 2015 Jake Spracher. All rights reserved.
//
import UIKit
import AVFoundation
protocol SnapContainerViewControllerDelegate {
// print "Snapcontainerview"
func outerScrollViewShouldScroll() -> Bool
}
class SnapContainerViewController: UIViewController, UIScrollViewDelegate {
var captureSession : AVCaptureSession!
private var current: UIViewController
// print "2"
var deeplink: DeeplinkType? {
didSet {
handleDeeplink()
}
}
init() {
current = SplashViewController()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var topVc: UIViewController?
var leftVc: UIViewController!
var middleVc: UIViewController!
var rightVc: UIViewController!
var bottomVc: UIViewController?
var directionLockDisabled: Bool!
var horizontalViews = [UIViewController]()
var veritcalViews = [UIViewController]()
var initialContentOffset = CGPoint() // scrollView initial offset
var middleVertScrollVc: VerticalScrollViewController!
var scrollView: UIScrollView!
var delegate: SnapContainerViewControllerDelegate?
class func containerViewWith(_ leftVC: UIViewController,
middleVC: UIViewController,
rightVC: UIViewController,
topVC: UIViewController?=nil,
bottomVC: UIViewController?=nil,
directionLockDisabled: Bool?=false) -> SnapContainerViewController {
let container = SnapContainerViewController()
container.directionLockDisabled = directionLockDisabled
container.topVc = topVC
container.leftVc = leftVC
container.middleVc = middleVC
container.rightVc = rightVC
container.bottomVc = bottomVC
return container
}
var scrollOffSetClosure: ((_ offset: CGFloat) -> Void)?
// code truncated for brevity
func scrollViewDidScroll2(_ scrollView: UIScrollView) {
scrollOffSetClosure!(scrollView.contentOffset.x)
print("test here")
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
print("end scroll")
}
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(current)
current.view.frame = view.bounds
view.addSubview(current.view)
current.didMove(toParentViewController: self)
}
func setupVerticalScrollView() {
middleVertScrollVc = VerticalScrollViewController.verticalScrollVcWith(middleVc: middleVc as! Camera,
topVc: topVc,
bottomVc: bottomVc)
delegate = middleVertScrollVc
}
func setupHorizontalScrollView() {
scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.bounces = false
print ("6")
let view = (
x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height
)
scrollView.frame = CGRect(x: view.x,
y: view.y,
width: view.width,
height: view.height
)
self.view.addSubview(scrollView)
let scrollWidth = 3 * view.width
let scrollHeight = view.height
scrollView.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
leftVc.view.frame = CGRect(x: 0,
y: 0,
width: view.width,
height: view.height
)
middleVertScrollVc.view.frame = CGRect(x: view.width,
y: 0,
width: view.width,
height: view.height
)
rightVc.view.frame = CGRect(x: 2 * view.width,
y: 0,
width: view.width,
height: view.height
)
addChildViewController(leftVc)
addChildViewController(middleVertScrollVc)
addChildViewController(rightVc)
scrollView.addSubview(leftVc.view)
scrollView.addSubview(middleVertScrollVc.view)
scrollView.addSubview(rightVc.view)
leftVc.didMove(toParentViewController: self)
middleVertScrollVc.didMove(toParentViewController: self)
rightVc.didMove(toParentViewController: self)
scrollView.contentOffset.x = middleVertScrollVc.view.frame.origin.x
scrollView.delegate = self
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
// print ("Scrollviewvillbegindragging scrollView.contentOffset \(scrollView.contentOffset)")
self.initialContentOffset = scrollView.contentOffset
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if delegate != nil && !delegate!.outerScrollViewShouldScroll() && !directionLockDisabled {
let newOffset = CGPoint(x: self.initialContentOffset.x, y: self.initialContentOffset.y)
// print(newOffset.x)
//print(newOffset.y)
// Setting the new offset to the scrollView makes it behave like a proper
// directional lock, that allows you to scroll in only one direction at any given time
self.scrollView!.setContentOffset(newOffset, animated: false)
// print ("newOffset \(newOffset)")
// tell child views they have appeared / disappeared
}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 667.0) {print ("aaa!")}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: -667.0) {print ("bbb!")}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 375) {print ("aaccca!")}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: -375) {print ("ddddd!")}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 0.0) {print ("eeeeeee!")}
// if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 667.0) {print ("ffffff!")}
// if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 667.0) {print ("aggggggggaa!")}
// print ("scrollViewDidScroll scrollView.contentOffset \(scrollView.contentOffset)")
// scrollView.contentOffset
if (scrollView.contentOffset) == CGPoint(x: 750.0, y: 0.0) {
print ("TEST OK LEFT!")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadRinkbox"), object: nil)
}
if (scrollView.contentOffset) == CGPoint(x: 0.0, y: 0.0) {
print ("TEST OK RIGHT!")
}
if (scrollView.contentOffset) == CGPoint(x: -750.0, y: 0.0) {
print ("TEST OK LEFT")
}
if (scrollView.contentOffset) == CGPoint(x: 375, y: 0.0) {
print ("TEST LEFT/RIGHT OK!")
}
}
private func animateFadeTransition(to new: UIViewController, completion: (() -> Void)? = nil) {
print ("Ca va dans animateFadeTransition")
current.willMove(toParentViewController: nil)
addChildViewController(new)
transition(from: current, to: new, duration: 0.3, options: [.transitionCrossDissolve, .curveEaseOut], animations: {
}) { completed in
self.current.removeFromParentViewController()
new.didMove(toParentViewController: self)
self.current = new
completion?()
}
}
private func animateDismissTransition(to new: UIViewController, completion: (() -> Void)? = nil) {
print ("Ca va dans animateDismissTransition")
let initialFrame = CGRect(x: -view.bounds.width, y: 0, width: view.bounds.width, height: view.bounds.height)
current.willMove(toParentViewController: nil)
addChildViewController(new)
new.view.frame = initialFrame
transition(from: current, to: new, duration: 0.3, options: [], animations: {
new.view.frame = self.view.bounds
}) { completed in
self.current.removeFromParentViewController()
new.didMove(toParentViewController: self)
self.current = new
completion?()
}
}
}
and VerticalScrollViewController
/ MiddleScrollViewController.swift
// SnapchatSwipeView
//
// Created by Jake Spracher on 12/14/15.
// Copyright © 2015 Jake Spracher. All rights reserved.
//
import UIKit
class VerticalScrollViewController: UIViewController, SnapContainerViewControllerDelegate {
var topVc: UIViewController!
var middleVc: UIViewController!
var bottomVc: UIViewController!
var scrollView: UIScrollView!
class func verticalScrollVcWith(middleVc: UIViewController,
topVc: UIViewController?=nil,
bottomVc: UIViewController?=nil) -> VerticalScrollViewController {
let middleScrollVc = VerticalScrollViewController()
middleScrollVc.topVc = topVc
middleScrollVc.middleVc = middleVc
middleScrollVc.bottomVc = bottomVc
return middleScrollVc
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view:
setupScrollView()
}
func setupScrollView() {
scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.showsVerticalScrollIndicator = false
scrollView.bounces = false
let view = (
x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height
)
scrollView.frame = CGRect(x: view.x, y: view.y, width: view.width, height: view.height)
self.view.addSubview(scrollView)
let scrollWidth: CGFloat = view.width
var scrollHeight: CGFloat
switch (topVc, bottomVc) {
case (nil, nil):
scrollHeight = view.height
middleVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
addChildViewController(middleVc)
scrollView.addSubview(middleVc.view)
middleVc.didMove(toParentViewController: self)
case (_?, nil):
scrollHeight = 2 * view.height
topVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
middleVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
addChildViewController(topVc)
addChildViewController(middleVc)
scrollView.addSubview(topVc.view)
scrollView.addSubview(middleVc.view)
topVc.didMove(toParentViewController: self)
middleVc.didMove(toParentViewController: self)
scrollView.contentOffset.y = middleVc.view.frame.origin.y
case (nil, _?):
scrollHeight = 2 * view.height
middleVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
bottomVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
addChildViewController(middleVc)
addChildViewController(bottomVc)
scrollView.addSubview(middleVc.view)
scrollView.addSubview(bottomVc.view)
middleVc.didMove(toParentViewController: self)
bottomVc.didMove(toParentViewController: self)
scrollView.contentOffset.y = 0
default:
scrollHeight = 3 * view.height
topVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
middleVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
bottomVc.view.frame = CGRect(x: 0, y: 2 * view.height, width: view.width, height: view.height)
addChildViewController(topVc)
addChildViewController(middleVc)
addChildViewController(bottomVc)
scrollView.addSubview(topVc.view)
scrollView.addSubview(middleVc.view)
scrollView.addSubview(bottomVc.view)
topVc.didMove(toParentViewController: self)
middleVc.didMove(toParentViewController: self)
bottomVc.didMove(toParentViewController: self)
scrollView.contentOffset.y = middleVc.view.frame.origin.y
}
scrollView.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
}
// MARK: - SnapContainerViewControllerDelegate Methods
func outerScrollViewShouldScroll() -> Bool {
if scrollView.contentOffset.y < middleVc.view.frame.origin.y || scrollView.contentOffset.y > 2*middleVc.view.frame.origin.y {
return false
} else {
return true
}
}
}
In MiddleScrollViewController.swift, make the controller conform to UIScrollViewDelegate:
class VerticalScrollViewController: UIViewController,
SnapContainerViewControllerDelegate,
UIScrollViewDelegate {
In that class, in setupScrollView(), set the delegate:
func setupScrollView() {
scrollView = UIScrollView()
// set the delegate to self
scrollView.delegate = self
// the rest of the existing code...
Still in that class, implement didScroll (or whatever other delegate funcs you want to handle):
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("Vertical Scroll - contentOffset:", scrollView.contentOffset)
}
Now you should get lots of print lines in the debug console when you scroll vertically:
Vertical Scroll - contentOffset: (0.0, 561.0)
Vertical Scroll - contentOffset: (0.0, 560.0)
Vertical Scroll - contentOffset: (0.0, 559.0)
Vertical Scroll - contentOffset: (0.0, 558.0)
If you want your SnapContainerViewController class to be informed when vertical scrolling takes place, you'll probably want to use a new protocol/delegate so VerticalScrollViewController can send that information.

Why my UILabel doesn't conforms to the inital frame?

I'm trying to create a popup label over the imageview. But in my application it doesn't work. I've made a test application with the same screen: UIImageView and below a UIView with a UIButton on the view.
So, where are two questions.
What could be difference in a code for such a different behaviour?
Why in my application the UILabel doesn't conforms to the initial frame?
The code of the function inside my viewController is the same:
private func showBanner(startY: CGFloat, targetView: UIView) {
let height: CGFloat = 42
let finishY = startY - height
let bannerLabel = UILabel(frame: CGRect(x: 0, y: startY, width: self.view.frame.width, height: height))
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
bannerLabel.font = UIFont.systemFont(ofSize: 13, weight: .regular)
bannerLabel.textColor = .lightGray
bannerLabel.backgroundColor = .black
bannerLabel.textAlignment = .center
bannerLabel.numberOfLines = 1
bannerLabel.text = "You've added the item to the favorites"
targetView.addSubview(bannerLabel)
UIView.animate(withDuration: 0.5, animations: {
bannerLabel.frame = CGRect(x: 0,
y: finishY,
width: self.view.frame.width,
height: height
)
}) {
_ in
UIView.animate(withDuration: 0.5, delay: 1.0, options: .curveLinear, animations: {
bannerLabel.frame = CGRect(x: 0,
y: startY,
width: self.view.frame.width,
height: height
)
}, completion: {
_ in
bannerLabel.removeFromSuperview()
})
}
}
The function is being called so:
showBanner(startY: itemsImageView.frame.maxY, targetView: itemsImageView)
The problem was in the line:
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
As soon as this line was removed, the problem has dissapeared.

Custom view doesn't appear for the first time swift

When user open my app for the first time, my custom view doesn't appear. But the next time it works fine.
I am calling this view when user taps on button.
let menu = MenuView(image: image, title: "text", buttons: buttons)
menu.show(animated: true)
Custom View code
class MenuView: UIView, Menu {
var background = UIView()
var blackOverlay = UIView()
convenience init(image: UIImage, title: String, buttons: [UIButton]) {
self.init(frame: UIScreen.main.bounds)
setupView(image: image, title: title, buttons: buttons)
}
#objc func cancelTapped() {
hide(animated: true)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(image: UIImage, title: String, buttons: [UIButton]) {
blackOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5)
blackOverlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(blackOverlay)
let backgroundWidth = self.frame.width - CGFloat(80.0)
let imageView = UIImageView(frame: CGRect(x: (backgroundWidth/2)-17, y: 40, width: 34, height: 34))
imageView.image = image
imageView.contentMode = .center
background.addSubview(imageView)
let titleLabel = UILabel()
let stringHeight = title.stringHeight + 14
titleLabel.frame = CGRect(x: background.center.x+8, y: 100, width: backgroundWidth-16, height: stringHeight)
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
background.addSubview(titleLabel)
var newHeight: CGFloat = 0
for i in 0...buttons.count-1 {
buttons[i].frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
buttons[i].frame.size = CGSize(width: backgroundWidth, height: 50)
buttons[i].setTitleColor(.gingerColor, for: .normal)
buttons[i].setTitleColor(UIColor.gingerColor.withAlphaComponent(0.5), for: .highlighted)
buttons[i].titleLabel?.textAlignment = .center
newHeight+=buttons[i].frame.height
let separator = UIView()
separator.frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
separator.frame.size = CGSize(width: frame.width, height: 1)
separator.backgroundColor = UIColor(hexString: "dedede")
background.addSubview(separator)
if i == buttons.count-1 {
buttons[i].setTitleColor(UIColor(hexString: "9E9E9E"), for: .normal)
}
buttons[i].addTarget(self, action: #selector(self.cancelTapped), for: .touchUpInside)
background.addSubview(buttons[i])
}
background.frame.origin = CGPoint(x: center.x, y: frame.height)
background.frame.size = CGSize(width: frame.width-80, height: 90 + titleLabel.frame.height+imageView.frame.height + CGFloat(newHeight))
background.backgroundColor = .white
background.layer.cornerRadius = 16
background.layer.masksToBounds = true
addSubview(background)
}
}
and my custom view protocol & extension code:
protocol Menu {
func show(animated: Bool)
func hide(animated: Bool)
var blackOverlay: UIView { get }
var background: UIView { get }
}
extension Menu where Self: UIView {
func show(animated: Bool) {
self.blackOverlay.alpha = 0
self.background.alpha = 0
self.blackOverlay.center = self.center
self.background.center = CGPoint(x: self.center.x, y: self.frame.height+self.background.frame.height/2)
UIApplication.shared.delegate?.window??.rootViewController?.view.addSubview(self)
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 1
self.background.alpha = 1
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = self.center
}, completion: { (completed) in
print("completed is \(completed)")
})
} else {
self.blackOverlay.alpha = 1
self.background.alpha = 1
self.background.center = self.center
}
}
func hide(animated: Bool) {
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 0
self.background.alpha = 0
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = CGPoint(x: self.center.x, y: self.frame.height + self.background.frame.height/2)
}, completion: { (completed) in
self.removeFromSuperview()
})
} else {
self.removeFromSuperview()
}
}
}
As you can see I have a parameter completed in completion animation block, it returns false for the first time. All subsequent times it returns false
I found my mistake.
I tried to present my custom view controller above rootController. But, for the very first time user's rootcontroller is the first screen of onboarding. So, my custom view tried to be shown on another controller.

How to implement video controller like YouTube with rotation to landscape

I want to implement controller like YouTube. Where Top part of controller plays video and bottom remains static. And when user rotate device only Top gets rotates.
I tried to implement it with this code:
#IBOutlet weak var myView: UIView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
#objc func rotated() {
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
UIView.animate(withDuration: 0.9, animations: {
self.myView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
let rect = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
self.myView.frame = rect
})
}
}
And I have this result:
Problem is controller still in portrait orientation and status bar at wrong side.
I think that there is better implementation for this thing.
Please, help
This is a simple solution for your problem,
import UIKit
class ViewController: UIViewController {
let RotatingView :UIView = UIView()
let TextLabel :UILabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.RotatingView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: (self.view.frame.size.height/3))
self.RotatingView.backgroundColor = UIColor.black
self.TextLabel.text = "Rotating View"
self.TextLabel.textColor = UIColor.white
self.TextLabel.textAlignment = .center
self.TextLabel.frame = CGRect(x: 0, y: self.RotatingView.frame.size.height/2, width: self.RotatingView.frame.width, height: 20)
self.RotatingView.addSubview(self.TextLabel)
self.view.addSubview(self.RotatingView)
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func rotated(){
switch UIDevice.current.orientation {
case .landscapeLeft, .landscapeRight:
self.RotatingView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: (self.view.frame.size.height))
self.TextLabel.frame = CGRect(x: 0, y: self.RotatingView.frame.size.height/2, width: self.RotatingView.frame.width, height: 20)
default:
self.RotatingView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: (self.view.frame.size.height/3))
self.TextLabel.frame = CGRect(x: 0, y: self.RotatingView.frame.size.height/2, width: self.RotatingView.frame.width, height: 20)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.setNeedsStatusBarAppearanceUpdate()
}
override var prefersStatusBarHidden : Bool {
return false
}
}

Resources