add UIViewController .xib to UIViewController on button press - swift

in my game i have a ViewController that displays various items in a collectionView i need to make it so that when an item is pressed at an index...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
if indexPath.item == 0 {
//add a viewcontroller for viewing content
//other things here for customizing that view data
}
}
...a viewcontroller pops onscreen not taking up the whole screen but a small portion in the middle of the main ViewController
(i need the view to be reusable and adaptable) i have tried making a viewController and adding it to the main ViewController as a subview of sorts but no luck
i want to display different info depending what cell is selected if you could help me i would appreciate it

As far as I understand , You want to open custom view which is loaded from xib on your ViewControllers view or a different ViewControllers view on your ViewController(that display various item in collectionView).
If yes then use below code
//Create optional property
var myViewController : YourCustomViewController?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
if indexPath.item == 0 {
//add below line to load custom View Xib
loadCustomView(onView:self.view)
//OR To Add ViewController View use below code
loadViewController(onView:self.view)
}
}
fun loadCustomView(onView:UIView) {
let allViewsInXibArray = Bundle.init(for: type(of: self)).loadNibNamed("CustomView", owner: self, options: nil)
//If you only have one view in the xib and you set it's class to MyView class
let myCustomView = allViewsInXibArray?.first as! CustomView
onView addSubview(myCustomView)
addConstraints(onView: myCustomView)
}
func loadViewController(onView:UIView) {
myViewController = YourCustomViewController(nibName: "TestViewController", bundle: nil);
onView addSubview((myViewController?.view)!)
addConstraints(onView: (myViewController?.view)!)
}
func addConstraints(onView : UIView) {
onView.translatesAutoresizingMaskIntoConstraints = false;
let widthConstraint = NSLayoutConstraint(item: onView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal,
toItem: onView.superview, attribute: .width, multiplier: 0.8, constant: 0)
let heightConstraint = NSLayoutConstraint(item: onView, attribute: .height, relatedBy: .equal,
toItem: onView.superview, attribute: .height, multiplier: 0.6, constant: 0)
let xConstraint = NSLayoutConstraint(item: onView, attribute: .centerX, relatedBy: .equal, toItem:onView.superview , attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: onView, attribute: .centerY, relatedBy: .equal, toItem: onView.superview, attribute: .centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, xConstraint, yConstraint])
}
Further you can add popup and popdown Animation to your custom view.
Comment me if you need anything else.

Related

Hide view in iOS and rearrange other view with fullscreen

Let's assume I have this layout design on an iPhone Portrait.
A Label (red) and an Image (blue).
But I would like to have it this way when the iPhone is in landscape. Only the image (blue).
I trying with following code but the label does not appear after return back to portrait:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
let orient = UIDevice.current.orientation
switch orient {
case .portrait:
print("Portrait")
let ctop = NSLayoutConstraint(item: self.imageView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 44)
self.view.removeConstraint(ctop)
self.labelView.isHidden = false
break
default:
print("LandScape")
let ctop = NSLayoutConstraint(item: self.imageView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0)
self.view.addConstraint(ctop)
self.labelView.isHidden = true
break
}
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
})
}
You have a mixup with your addConstraint/removeConstraint logic. In the Portrait case you define a new cTop constraint and remove it (!?!) from the view - you probably want to add it.
It would be better to define both constraints right away when you create your views, and only set one to active - and then switch the isActive setting on both according to your new orientation. Or only define one constraint and just change the constant on it accordingly.

Adding views dynamically to UIStackview

I'm trying to add views(or buttons) to UIStackView dynamically.
At first, the UIStackView has no arranged views (vertically), and
after getting from some http response, several views(buttons) are added to UIStackView.
UIStackView is also autolayout to hold a specific area.
I've tried to find dynamic adding example, but failed.
Anyone can show me the examples of adding view onto UIStackView dynamically?
It may help you. Please follow this points:
Add UIScrollView to your UIViewController in storyboard or XIB.
Initiate an NSMutableArray name it arrViews gets server response and adds view in the array.
Initialise UIStackViewpass arrView array in the init method.
After that UIStackView will be added subview of UIScrollView.
Add constraint programmatically to UIStackView. That's it.
if let response = self.serverResponse {
if let body = response.responseBody {
if let view = body.views {
arrViews = createSubViews(view)
}
}
}
let stackView = UIStackView(arrangedSubviews: arrViews)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 16
stackView.distribution = .fill
self.scrollView.addSubview(stackView)
//constraints
let leading = NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: self.scrollView, attribute: .leading, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(leading)
let trailing = NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: self.scrollView, attribute: .trailing, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(trailing)
let top = NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.scrollView, attribute: .top, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(top)
let bottom = NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: self.scrollView, attribute: .bottom, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(bottom)
let equalWidth = NSLayoutConstraint(item: stackView, attribute: .width, relatedBy: .equal, toItem: self.scrollView, attribute: .width, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(equalWidth)
leading.isActive = true
trailing.isActive = true
top.isActive = true
bottom.isActive = true
equalWidth.isActive = true
Hope it will help you. Happy coding :)
I use this code in one of my projects:
let baseFrame = CGRect(origin: .zero, size: CGSize(width: requiredWidth, height: partitionHeight))
for instrument in instruments {
let partitionView = PartitionOnDemand(instrument: instrument, mode: playbackMode, frame: baseFrame, referenceView: partitionsAnimator)
partitionsStackView.addArrangedSubview(partitionView)
let tab = InstrumentInfoTabContainer.instantiate(with: instrument) {
self.focus(on: instrument)
}
tabsStackView.addArrangedSubview(tab)
}
While trying with answers, I happend to find how to work it.
class ViewController: UIViewController {
#IBOutlet weak var stack: UIStackView!
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.
}
#IBAction func onBtn_Create(_ sender: Any) {
createButton("new button ...")
}
#IBAction func onBtn_Delete(_ sender: Any) {
if let v = stack.arrangedSubviews.last {
stack.removeArrangedSubview(v)
v.removeFromSuperview()
}
}
func createButton(_ title: String) {
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
button.backgroundColor = UIColor.blue
button.setTitle(title, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
stack.addArrangedSubview(button)
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
}
And, I anchored to UIStackView, Trailing=0, Leading=0, Top=0, Bottom=8 to TextView.Top
The subviews inside it are intact without any constraints.
Thank you.

Programmatically created NSCollectionView `makeItem` returns nil even if registered

I am developing a macOS app targeting macOS 10.10 SDK and using Xcode 9.3 (Swift 4). I am not using xibs, but creating all views programmatically.
I want to create a NSCollectionView. I register a subclass of NSCollectionViewItem and then register that class to the NSCollectionView with a call to collectionView.register(:,forItemWithIdentifier). Later, in the data source, I call collectionView.makeItem(withIdentifier:,for:).
However the makeItem method always returns nil. What am I doing wrong?
I found a similar question but the solution is to call register, which I already do.
For reference, here's the minimum working to reproduce my the issue: when I put a breakpoint after the call to makeItem, I can see that the returned value is always nil.
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
window.contentViewController = TestViewController()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "testIdentifier")
class TestViewController: NSViewController, NSCollectionViewDataSource {
override func loadView() {
self.view = NSView()
}
override func viewDidLoad() {
let scroll = NSScrollView()
self.view.addSubview(scroll)
scroll.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: scroll, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 500).isActive = true
NSLayoutConstraint(item: scroll, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 500).isActive = true
let collection = NSCollectionView()
scroll.documentView = collection
collection.register(TestViewItem.self, forItemWithIdentifier: cellIdentifier)
collection.dataSource = self
collection.collectionViewLayout = NSCollectionViewFlowLayout()
collection.reloadData()
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: cellIdentifier, for: indexPath)
return item
}
}
class TestViewItem: NSCollectionViewItem {
override func loadView() {
self.view = NSView()
}
}
I think this is a bug. Workaround: set collectionViewLayout before registering the class.
Apparently the registered class is not stored if collectionViewLayout is not set. collectionViewLayout can be set to a new layout after registering the class, the registered class isn't removed.

UIButton is outside of ContainerView

I'm new to Swift, and I'm trying to learn how to build iOS apps programmatically, with very limited use of the Storyboard. Right now, the goal is to create a menu at the top of the app, which will contain buttons. Based off of my research, it appears that the best way to do this is to nest the UIButtons inside of a ContainerView. However, the issue is that my UIButton is displayed outside of my ContainerView. Perhaps I need to set constraints on the ViewContainer and my UIButton?
Could you guys please point me in the right direction? Your help is very much appreciated.
Storyboard:
App Simulator (iPhone 7 Plus):
HomeController.swift:
class HomeController: UIViewController {
let topMenuContainerView = UIView()
topMenuContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topMenuContainerView)
let topMenuController = storyboard!.instantiateViewController(withIdentifier: "Top_Menu_View_Controller")
addChildViewController(topMenuController)
topMenuController.view.translatesAutoresizingMaskIntoConstraints = false
topMenuContainerView.addSubview(topMenuController.view)
topMenuController.didMove(toParentViewController: self)
}
TopMenuController.swift:
class TopMenuController: UIViewController {
let createAdButton: UIButton = UIButton(type: UIButtonType.roundedRect)
createAdButton.setTitle("Create Ad", for: UIControlState.normal)
createAdButton.titleLabel?.textColor = UIColor.white
createAdButton.frame = view.frame
createAdButton.backgroundColor = UIColor.white
view.addSubview(createAdButton)
}
Yes, I believe adding constraints will solve your problem. It would be something like this:
let horizontalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerX, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerY, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
view.addConstraints([horizontalConstraint, verticalConstraint])
BUT, wouldn't it be better to have just a UIView inside your Home Controller instead of a View Controller just for the menu?

Swift3 setting constraints relative to parent view of parent view

I have 3 classes, one is a a normal UIViewController in in which I have this code:
let CC0 = CustomUIView0()
self.addSubview(CC0)
the CustomUIView00 is a UIView class containing this code:
let CC1 = CustomUIView1()
self.addSubview(CC1)
Now for CC1 I would like to activate a constraint like this
NSLayoutConstraint.activate([
NSLayoutConstraint(item: CC1, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: ("Parent view of CC0"), attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
])
What I tried to do is to use the UIScreen.main in the toItem place but this throws an error saying that the item needs to be an instance of UIView for this to work.
So I would essentially like to create a constraint relative to the parent of the paren view, how could I do this.
Update:
UIViewController:
let CC0 = UICustomUIView0()
self.view.insertSubview(self.CC0, at: 0)
UICustomUIView0:
init {
super.init(frame: UIScreen.main.bounds);
let CC1 = UICustomUIView1()
guard let CC1Parent = self.CC1.superview, let CC0Parent = CC1Parent.superview else {
print("Problem")
return
}
self.view.addSubview(CC1)
NSLayoutConstraint.activate([
NSLayoutConstraint(item: CC1, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: CC0Parent, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
])
}
This throws the problem print
Try below:
// guard below ensures you have the parent AND grandparent views
guard let cc1Parent = cc1.superview, let cc0Parent = cc1Parent.superview else {
// problem with accessing parent views
return
}
// now just set your constraints as per below
NSLayoutConstraint(item: cc1, attribute: .top, relatedBy: .equal, toItem: cc1Parent, attribute: .top, multiplier: 1, constant: 0)
EDIT: For UIView put code in awakeFromNib()
override func awakeFromNib() {
super.awakeFromNib()
// Set layout constraints here
}