How to programmatically scroll to next view controller in Scroll View Container - swift

I created a scroll view container that houses three view controllers. It's meant to mimic snapchat's swipe layout. however, I can't seem to get a code to manually switch to the next view controller without actually swiping (which I'm not interested in)
I tried calling the container class and setting it's scroll offset but it crashes... tried creating a delegate protocol, but delegate is returning nil... I'm stumped.
Here is my code:
class AViewController: UIViewController, ABViewControllerDelegate {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// 1) Create the three views used in the swipe container view
var ATVc : ATViewController = ATViewController(nibName: "ATViewController", bundle: nil);
var ACVc : ACViewController = ACViewController(nibName: "ACViewController", bundle: nil);
var ABVc : ABViewController = ABViewController(nibName: "ABViewController", bundle: nil);
// 2) Add in each view to the container view hierarchy
// Add them in opposite order since the view hieracrhy is a stack
self.addChildViewController(ABVc);
self.scrollView!.addSubview(ABVc.view);
ABVc.didMoveToParentViewController(self);
self.addChildViewController(ACVc);
self.scrollView!.addSubview(ACVc.view);
ACVc.didMoveToParentViewController(self);
self.addChildViewController(ATVc);
self.scrollView!.addSubview(ATVc.view);
ATVc.didMoveToParentViewController(self)
// 3) Set up the frames of the view controllers to align
// with eachother inside the container view
var adminFrame :CGRect = ATVc.view.frame;
adminFrame.origin.y = adminFrame.height;
ACVc.view.frame = adminFrame;
var BFrame :CGRect = ACVc.view.frame;
BFrame.origin.y = 2*BFrame.height;
ABVc.view.frame = BFrame;
// 4) Finally set the size of the scroll view that contains the frames
var scrollWidth: CGFloat = self.view.frame.width
var scrollHeight: CGFloat = 3 * self.view.frame.size.height
self.scrollView!.contentSize = CGSizeMake(scrollWidth, scrollHeight)
self.scrollView!.setContentOffset(CGPointMake(0, self.view.frame.height), animated: false)
var changeMe : String = "okay"
}
func scrollUp() {
println("clicked!")
self.scrollView.contentOffset.y - self.view.frame.height
}
}
and this is the view controller I'm trying to get out off by pressing a button..
protocol ABViewControllerDelegate {
func scrollUp()
}
class ABViewController: UIViewController {
let delegate = ABViewControllerDelegate?()
#IBAction func button(sender: AnyObject) {
println("button clicked!")
delegate!.scrollUp()
}
}
I feel like I'm leading myself on and that it can't be done!

Related

Swift iPad - How to present a formSheet with custom size based on a table view content?

In my app i present a form sheet in this way for iPad devices:
myViewController = .formSheet
present(myViewController, animated: true)
This ViewController is very simple: there is just a table view displaying n elements.
My goal is make the formSheet size the same as the table view with its n elements.
I actually achieved that with this code:
class MyViewControlller: UIViewController {
#IBOutlet weak var myTable: UITableView!
private var observer: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
myTable.delegate = self
myTable.dataSource = self
observer = myTable(\.contentSize, options: [.new], changeHandler: { [weak self] (table, _) in
guard let self = self else {
return
}
self.preferredContentSize = CGSize(width: table.contentSize.width, height: table.contentSize.height + 30.0)
self.view.layoutIfNeeded()
})
}
}
But the problem is that while presenting the formSheet (while it is sliding from bottom to the center of the screen) I can clearly see an animation where the bottom of the sheet is adjusting its size until it reaches the center of the screen.
What I'm trying to achieve is this: When the sheet starts sliding from the bottom to the center of the screen it already has the right dimensions.
Am I doing something wrong with the code?

How to use a button created in XIB custom view to control setContentOffset in viewController.swift?

I followed this tutorial to create a Snapchat-Like Menu: https://www.youtube.com/watch?v=1_daE3IL_1s
In short, the idea was to create custom views with XIB, and add them into a scrollview.
I would like to add a "back" button to one of the custom views so that the scrollview will automatically scroll back to its initial view when the button is tapped. The Idea I had was to write a code inside the custom view's IBAction function to call UIScrollview's content offset delegate function.
the following is the code in View Controller:
class ViewController: UIViewController, UIScrollViewDelegate {
//I'm naming the Scroll View as "scrollView"
#IBOutlet weak var scrollView: UIScrollView! {
didSet{
scrollView.delegate = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var v1 : View1 = View1(nibName: "View1", bundle: nil)
var v2 : View2 = View2(nibName: "View2", bundle: nil)
self.addChild(v1)
self.scrollView.addSubview(v1.view)
v1.didMove(toParent: self)
self.addChild(v2)
self.scrollView.addSubview(v2.view)
v1.didMove(toParent: self)
var v2Frame : CGRect = v2.view.frame
v2Frame.origin.x = self.view.frame.width
v2.view.frame = v2Frame
self.scrollView.contentSize = CGSize(width: self.view.frame.width * 2, height: self.view.frame.height)
// self.scrollView.contentSize = CGSize(self.view.frame.width * 2, self.view.frame.size.height)
}
}
The following ht the code in View2:
class View2: UIViewController {
#IBOutlet weak var buttonHome: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonHomeAction(_ sender: Any) {
//how to access scrollview.setContentOffset from ViewController to scroll the view back to the first view?
}
So my question is how to access scrollview.setContentOffset from ViewController to scroll the view back to the first view? Am I on the right track? Or are there any alternative methods where I can tap a button and scroll the view back to its first page?
Thank you!
I can think of two good options here:
1) Hold a weak reference in View2 of the scrollView. in View2 add:
weak var scrollView: UIScrollView?
and in ViewController, after view2 creation add:
view2.scrollView = self.scrollView
now, in buttonHomeAction you'll have access to the scrollView.
2) If you are familiar with Apple's delegation pattern, you can add a delegate, which can be a weak reference of the presenting view controller.
Create a protocol for the delegate, using named by the view holds the delegate. so, in this case: View2Delegate
and add a method that describes the action.
protocol View2Delegate: AnyObject {
func buttonTapped()
}
so, in View2 add:
weak var delegate: View2Delegate?
and in ViewController, after view2 creation add:
view2.delegate = self
now, in ViewController implement the protocol method and access scrollView:
extension ViewController: View2Delegate {
func buttonTapped() {
// access scrollView as self.scrollView
}
}

Dynamically adding to NSStackView and Adding Scrolling Functionality

Like the title says, I am trying to dynamically add NSTextViews and NSImageViews to a NSStackView. First, I added a Scroll View using IB and that made a hierarchy of Bordered Scroll View -> Clip View -> Main View -> Stack View (Vertical). I want to be able to dynamically add views to the stack view and be able to scroll through them all. I used autolayout on the Bordered Scroll View.
The problem: I tested this by adding 100 NSTextViews and incrementing their y-positions to stack up on each other. But, I cannot scroll to the top. I have been trying to understand this for a couple days but I just cannot figure it out. I cannot scroll and see all the text views, but when I increase the window size I can see more of them. TIA! `
import Cocoa
class ViewController: NSViewController {
var xLoc = CGFloat(0)
var yLoc = CGFloat(0)
#IBOutlet weak var mainView: NSStackView!
#IBOutlet weak var belowMainView: NSStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
for index in 1...100 {
var txt = "Num: \(index)"
setup(text: txt)
}
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func setup(text: String){
let tempTextView = NSTextView(frame: NSMakeRect(xLoc, yLoc, 320, 10))
tempTextView.append(text)
tempTextView.isEditable = false
tempTextView.translatesAutoresizingMaskIntoConstraints = false
// mainView.addSubview(tempTextView)
belowMainView.addSubview(tempTextView)
yLoc += 20
}
}
// Adding an append function to textView functionality
extension NSTextView {
func append(_ string: String) {
self.textStorage?.append(NSAttributedString(string: string))
// Scrolls to end of document if it is out of view
self.scrollToEndOfDocument(nil)
}
}`

Reset/Refesh UIscroll view

I Have a scroll view that contains multiple view controllers. On those view controllers I have switches. When on switch is "on" all the other switches turn off. This is all done through the viewDidLoad.
I cannot get this to work though, I have tried putting the code for the switch thingy stuff that I am trying to do in viewWillAppear. It seems though that these two methods are only called at run time.
My alternative idea is to somehow "reset" the scroll view from inside one of the subviews
here is what i have:
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let vc0 = ViewController0(nibName: "ViewController0", bundle: nil)
self.addChildViewController(vc0)
self.scrollView.addSubview(vc0.view)
vc0.didMoveToParentViewController(self)
let vc1 = ViewController1(nibName: "ViewController1", bundle:nil)
var frame1 = vc1.view.frame
frame1.origin.x = self.view.frame.size.width
vc1.view.frame = frame1
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
let vc2 = ViewController2(nibName: "ViewController2", bundle:nil)
var frame2 = vc2.view.frame
frame2.origin.x = self.view.frame.size.width * 2
vc2.view.frame = frame2
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
let vc3 = ViewController2(nibName: "ViewController3", bundle:nil)
var frame3 = vc3.view.frame
frame3.origin.x = self.view.frame.size.width * 3
vc3.view.frame = frame3
self.addChildViewController(vc3)
self.scrollView.addSubview(vc3.view)
vc3.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 4, self.view.frame.size.height - 66);
}
}
^for the scrollUI creator class
import UIKit
class ViewController0: UIViewController {
#IBOutlet weak var onoff: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
onoff.on = false
}
#IBAction func used(sender: AnyObject) {
let parent = self.view.superview
parent?.setNeedsDisplay()
}
}
^this is one of the subclasses
This subclass has a switch and when toggled, it should "reset" the scroll view so all of the subviews viewDidLoad are called again...
This does not work.... Are there any solutions to this??
BTW sorry for the bad formatting for the code blocks.

Displaying a UIAlertView adjacent to button pressed to call

I'm trying to figure out if theres a way to tie the location a programatically created UIAlertView is displayed on screen to the location of the button pressed to activate it.
I would like to have my alert view display directly below my button.
Thanks to gutenmorgenuhu for setting me on the right direction I was able to create a UIPopover that locates its self to the button pressed.
I first created a new UIViewController with the content of my popover (a UIPickerView in this case), that view is given the storyboard name Popover.
Then in my original view I created 2 buttons, topButton and bottomButton and linked them to their respective actions and outlets.
My code follows:
import UIKit
class ViewController: UIViewController, UIPopoverControllerDelegate, UIPopoverPresentationControllerDelegate {
//declare variables
var popOverLocation: CGRect!
// Set outlets for all buttons
#IBOutlet weak var topButton: UIButton!
#IBOutlet weak var bottomButton: UIButton!
//Add actions to buttons, first trigger popOverLocation to take the frame properties of the active button, then call the popover function.
#IBAction func topButton(sender: AnyObject) {
popOverLocation = topButton.frame
popOver()
}
#IBAction func bottomButton(sender: AnyObject) {
popOverLocation = bottomButton.frame
popOver()
}
//function for creating the popover
func popOver() {
//Read the height, and XY coordinates of the active button
var h = popOverLocation.height
var x = popOverLocation.origin.x
var y = popOverLocation.origin.y + h
//create the popover
var popoverContent = self.storyboard!.instantiateViewControllerWithIdentifier("Popover") as UIViewController
var nav = UINavigationController(rootViewController: popoverContent)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController! as UIPopoverPresentationController
popover.sourceView = self.view
popover.delegate = self
//set the size of the popover
popoverContent.preferredContentSize = CGSizeMake(200,300);
//set the direction the popover arrow is allowed to point
popover.permittedArrowDirections = UIPopoverArrowDirection.Up
//using the buttons coordinates set the location of the popover
popover.sourceRect = CGRectMake(x,y,0,0)
self.presentViewController(nav, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}