How to put collection view and page view within one Scene - ios

I am creating a screen like below:
But the problem is, pagedView requires my view controller to inherit from UIPagedViewController, while colleciton view requires inheriting from UICollectionViewController.
Is there a way to achieve this?

You don't have to use UIPagedViewController or UICollectionViewController make it inherit from UIViewController , and inside say loadView/ViewDidLoad use
let pager = UIPagedViewController()
// then add it as a child vc and constraint it's view or set a frame
and
let collec = UICollectionView(///
// add to view and constraint also
The above should be instance vars also , so their delegates/dataSources being retained

what about like this?
class MyPagedView: UIView, UIPageViewControllerDataSource {
// add your pages
}
class MyCollectionView: UICollectionView, UICollectionViewDataSource {
// add your collection view
}
class ViewController: UIViewController {
lazy var myPages: MyPagedView = {
let pages = MyPagedView()
return pages
}()
lazy var myCV: MyCollectionView = {
let cv = MyCollectionView()
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myPages)
view.addSubview(myCV)
}
}
Of Course, you will have to set your constraints according to your need.

Related

Update subviews of UIView from ViewController

I have a UIViewController that implements a custom UIView, so;
override func loadView() {
view = CustomView()
}
The custom view has a few lables and buttons and all the normal stuff, problem is in my viewController I have a request, and when that request is done, I'd like to update some of those lables/buttons.
Right now, in my CustomView, I have functions, such as;
func updateView() {
labelOne.isHidden = true
LabelTwo.isHidden = false
}
So I call the appropriate function from my viewController when the request is done.
This works, but it feels wrong, is there a neater way to update the subviews of my custom UIView, from my viewController? Should I maybe be using protocols or delegates?
One thing I've found quite neat in the past is passing the model directly to the custom view, then using didSet to trigger updates.
class CustomView: UIView {
let labelOne = UILabel()
let labelTwo = UILabel()
var object:CustomObject! {
didSet {
self.labelOne.text = object.name
self.labelTwo.text = object.description
}
}
...
}
This means in your UIViewController you can do the request and then pass the model straight to the custom view.
RequestHelper.getObject() { object in
self.customView.object = object
}
Obviously here I'm guessing at your request and object names but hopefully you get the idea.

Setting delegate for CustomUiView getting EXC_BAD_ACCESS

I have created a custom UIView and have a protocol set for it. Now from the View Controller when I set the delegate to self I am getting an EXC_BAD_ACCESS.
---The View Controller Code------
class VerificationController: UIViewController, LoadingViewDelegate {
#IBOutlet weak var instructionView: LoadingView!
override func viewDidLoad() {
super.viewDidLoad()
instructionView.randomTextIndexes = [1]
instructionView.delegate = self
}
...
}
// "instructionView" is the UIView outlet and "LoadingView" is the class
--This is the custom View Code ------
protocol LoadingViewDelegate {
func generated(random code:String)
}
class LoadingView: UIView {
var delegate:LoadingViewDelegate?
var randomTextIndexes:[Int] = []
}
I am getting EXC_BAD_ACCESS when I try to access the delegate as well as the randomTextIndexes from the viewDidLoad() method of the view controller. Could you please tell me what I am missing here.
My best guess is that you forgot to set the class of the custom view in the Interface Builder. Check the Identity Inspector for your object in the IB, it should look like this:
If it isn't set, then that's the problem.

Detecting ScrollOffset in ScrollView inside ViewController held within UIPageViewController?

I hava a UIPageViewController that holds an array of ContentUIViewController, these have scrollviews in them.
When the scrollview scrolls I want to recognise this in the view controller that is the parent of the UIPageViewController (added as child)
What would be the best route to achieve this? as currently I tried delegating the didScroll back to the viewmodel for the ContentUIViewController then feeding that into the parent of the UIPageViewController to adjust header height that I want to collapse, but it doesn't work well / is very hacky
Is there a way to read delegate output in the a child vc to the parent vc without feeding it back through viewmodels? This is proving tricky due to the array nature of the UIPageViewController rather than a single child VC.
Updated:
private func generateContentControllers() -> [UIViewController] {
var viewControllers: [UIViewController] = []
viewControllers.append(ScrollableContentViewController(contentViews: infoViews))
viewControllers.append(reviewsVC)
viewControllers.append(ScrollableContentViewController(contentViews: helpViews))
}
return viewControllers
}
Provided via
var scrollingViewControllers: [UIViewController] { get }
Added with
if let firstViewController = self.viewModel.scrollingViewControllers.first {
pagingController.setViewControllers([firstViewController], direction: .forward, animated: false)
}
Here is how you can get that working.
1. Create a handler in ContentUIViewController that will be called in scrollViewDidScroll(_:) method for each contentOffset change.
class ContentUIViewController: UIViewController, UIScrollViewDelegate {
var handler: ((CGPoint)->())?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
handler?(scrollView.contentOffset)
}
}
2. In ParentVC, create an instance of UIPageViewController and add it as a child to it. I think you did that already.
What you need to do next is create an array of ContentUIViewController and for each instance set the handler that we created earlier.
This handler will be called every time the scrollView is scrolled in that particular ContentUIViewController. We'll get the contentOffset of the scrollView here. Call updateHeaderHeight(for:) with the obtained contentOffset to adjust the header height.
class ViewModel {
private func generateContentControllers() -> [ContentUIViewController] {
var viewControllers: [ContentUIViewController] = []
//add your code here....
return viewControllers
}
lazy var scrollingViewControllers: [ContentUIViewController] = {
return self.generateContentControllers()
}()
}
class ParentVC: UIViewController {
let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.scrollingViewControllers.forEach {
$0.handler = {(offset) in
self.updateHeaderHeight(for: offset)
}
}
}
func updateHeaderHeight(for offset: CGPoint) {
//add the code to adjust header height here...
}
//add rest of the code for pageViewController here....
}

how to DownCasting UIViewController Swift

base ViewController
import UIKit
class SubViewPost: UIViewController {
#IBOutlet weak var content: UILabel!
#IBOutlet weak var recommendCount: UILabel!
#IBOutlet weak var recommendButton: UIButton!
var postInfo:PostInfo!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
child ViewController
import UIKit
class SubViewOne: SubViewPost {
#IBAction func likeWorry(_ sender: Any) {
Option.recommend(postInfo: postInfo, mRecommendCount: recommendCount, mRecommendButton: recommendButton)
}
}
and another child viewController
import UIKit
class SubViewTwo: SubViewPost {
override func viewDidLoad() {
recommendCount.alpha=0
recommendButton.alpha=0
}
}
i want add subviewOne or SubViewTwo
My ParentView
var subViewPost:SubViewPost
if postType == 1{
subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewOne
}else{
subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewTwo
}
containerView.addSubview(subViewPost.view)
raise error
Could not cast value of type
'MyApp.SubViewPost' (0x101151728) to 'MyApp.SubViewOne' (0x10114d9d0).
2018-07-10 14:40:56.007436+0900 MyApp[7207:209932]
Could not cast value of type 'MyApp.SubViewPost' (0x101151728) to 'MyApp.SubViewOne' (0x10114d9d0).
how to chagne view controller by According to postType
SubView One have Recommned
but SubView Two haven't Recommend
SubView 1,2 have same UI
The UViewController class for your scene "SubViewPost" in your storyboard is set to SubViewPost and that is what instantiateViewController will be returning. You cannot downcast an instance of SubViewPost to SubViewOne or SubViewTwo.
You could define two identical scenes in your storyboard, each with the appropriate view controller class, but that would require a lot of duplication.
Since the only difference is the visibility of the recommendButton and recommendCount elements, why not just handle that via a property:
var subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewPost
subViewPost.recommendVisible = (postType == 1)
SubViewPost.swift
var recommendVisible = true
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
recommendCount.isHidden = !recommendVisible
recommendButton.isHidden = !recommendVisible
}
The error message is clear. When you say
storyboard?.instantiateViewController(withIdentifier: "SubViewPost")
what you get from the storyboard is a view controller whose class is SubViewPost. You cannot wave a magic casting wand and claim that it is a SubViewOne instead; it isn't.
If you wanted this view controller to be a SubViewOne, then you should have declared it as a SubViewOne in the storyboard in the Identity inspector.
I think I see what you are trying to do, and why you are confused about why you can't do it this way.
What's in the storyboard is an instance, not a class. Yes, it is an instance of some class, but it is an instance of that class. So when you design the interface in the storyboard, you are designing the interface associated with that one instance of that one class.
If your goal is to have a single interface associated with multiple classes, the interface must be generated in code or loaded from a View .xib file — not designed in a storyboard.
However, you would be better off not trying to use subclassing in this situation in the first place. What I do in a similar situation is give my view controller an enum property that says which "kind" of view controller it is, and obey accordingly in code. That a way, a single class serves multiple purposes.

Passing data between views in ONE ViewController in Swift

All of the searches I've done focus on passing data between view controllers. That's not really what I'm trying to do. I have a ViewController that has multiple Views in it. The ViewController has a slider which works fine:
var throttleSetting = Float()
#IBAction func changeThrottleSetting(sender: UISlider)
{
throttleSetting = sender.value
}
Then, in one of the Views contained in that same ViewController, I have a basic line that (for now) sets an initial value which is used later in the DrawRect portion of the code:
var RPMPointerAngle: CGFloat {
var angle: CGFloat = 2.0
return angle
}
What I want to do is have the slider's value from the ViewController be passed to the View contained in the ViewController to allow the drawRect to be dynamic.
Thanks for your help!
EDIT: Sorry, when I created this answer I was having ViewControllers in mind. A much easier way would be to create a method in SomeView and talk directly to it.
Example:
class MainViewController: UIViewController {
var view1: SomeView!
var view2: SomeView!
override func viewDidLoad() {
super.viewDidLoad()
// Create the views here
view1 = SomeView()
view2 = SomeView()
view.addSubview(view1)
view.addSubview(view2)
}
#IBAction func someAction(sender: UIButton) {
view1.changeString("blabla")
}
}
class SomeView: UIView {
var someString: String?
func changeString(someText: String) {
someString = someText
}
}
Delegate:
First you create a protocol:
protocol NameOfDelegate: class { // ": class" isn't mandatory, but it is when you want to set the delegate property to weak
func someFunction() // this function has to be implemented in your MainViewController so it can access the properties and other methods in there
}
In your Views you have to add:
class SomeView: UIView, NameOfDelegate {
// your code
func someFunction() {
// change your slider settings
}
}
And the last step, you'll have to add a property of the delegate, so you can "talk" to it. Personally I imagine this property to be a gate of some sort, between the two classes so they can talk to each other.
class MainViewController: UIViewController {
weak var delegate: NameOfDelegate?
#IBAction func button(sender: UIButton) {
if delegate != nil {
let someString = delegate.someFunction()
}
}
}
I used a button here just to show how you could use the delegate. Just replace it with your slider to change the properties of your Views
EDIT: One thing I forgot to mention is, you'll somehow need to assign SomeView as the delegate. But like I said, I don't know how you're creating the views etc so I can't help you with that.
In the MVC model views can't communicate directly with each other.
There is always a view controller who manages the views. The views are just like the controllers minions.
All communication goes via a view controller.
If you want to react to some view changing, you can setup an IBAction. In the method you can then change your other view to which you might have an IBOutlet.
So in your example you might have an IBAction for the slider changing it's value (as in your original question) from which you could set some public properties on the view you would like to change. If necessary you could also call setNeedsDisplay() on the target view to make it redraw itself.

Resources