UIButton in scrollView position unexpected - ios

I created a very simple custom class from UIView. On the top there is a UIScrollView. Inside the UIScrollView, I placed a couple of buttons. The buttons position is out of the scrollview.
Here is the code.
class TestView: UIView {
var tabView: UIScrollView = UIScrollView()
var buttons = [UIButton]()
var tabHeight: CGFloat = 40.0
let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width
var titles = [String]()
var numberOfTitles = 0
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(tabView)
}
func createUI() {
tabView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: tabHeight)
tabView.backgroundColor = UIColor.grayColor()
tabView.contentSize = CGSize(width: screenWidth, height: tabHeight)
numberOfTitles = titles.count
for var i = 0; i < numberOfTitles; ++i {
var button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
button.frame = CGRect(x: 50.0*CGFloat(i), y: 0, width: 50.0, height: tabHeight)
button.setTitle(titles[i], forState: UIControlState.Normal)
button.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
tabView.addSubview(button)
}
}
}
Here is how it looks when I debug view Hierarchy.
But if I just change the UIScrollView to UIView, as below,
var tabView: UIView = UIView()
Then the button locations are correct, as below.
I could not figure out why. Please help. Thanks!

This is because UIScrollView by itself only is able to scroll a container view. What you need to do is initialize a UIScrollView like you did originally, but instead of adding all the components to the scrollview, create a new UIView called conatinerView, change all the tabView to containerView, then change the required init to:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(tabView)
tabView.addSubview(containerView)
}
But make sure the frame size for containerView is correct and so is the origin point.

Related

Not able to show border color and background as blur in custom view as popup view in Swift

I have some requirement to show popup as some customisation options.
So, I took custom view as UIView with Xib file.
class CustomAlertView : UIView {
#IBOutlet weak var customAlertView : UIView!
#IBOutlet weak var button1 : UIButton!
#IBOutlet weak var button2 : UIButton!
override init(frame: CGRect) { // for using CustomView in code
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) { // for using CustomView in IB
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("CustomAlertView", owner: self)
guard let content = customAlertView else { return }
content.frame = self.bounds
content.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(content)
}
}
And given outlet connections.
And I am able to load the view in viewcontroller class.
But, the issue is, its not showing as pop up view and its not showing any border color, etc even I added too.
And the present self.view (Main view from viewcontroller) still moving it has tableview, while I clicking on the buttons on custom view, nothing happening.
func someAction() {
self.view.addSubview(customAlert)
self.customAlert.layer.cornerRadius = 13
self.customAlert.layer.borderWidth = 10
self.customAlert.layer.borderColor = UIColor.gray.cgColor
self.customAlert.clipsToBounds = false
let radius: CGFloat = self.customAlert.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: self.customAlert.frame.height))
self.customAlert.layer.masksToBounds = false
self.customAlert.layer.cornerRadius = 8; // if you like rounded corners
self.customAlert.layer.shadowOffset = CGSize(width:-15, height:20);
self.customAlert.layer.shadowRadius = 5;
self.customAlert.layer.shadowOpacity = 0.5;
self.customAlert.layer.shadowPath = shadowPath.cgPath
self.customAlert.byMonthlyBtn.addTarget(self, action: #selector(button1tapped), for: .touchUpInside)
self.customAlert.byAnnuallyBtn.addTarget(self, action: #selector(button2tapped), for: .touchUpInside)
}
And its looks like below screenshot
Any suggestions?
private func commonInit() {
Bundle.main.loadNibNamed("CustomAlertView", owner: self)
guard let content = customAlertView else { return }
content.frame = self.bounds
content.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(content)
}
Your problem is you are using the CustomAlertView you loaded. Try adjust your code like below.
private func commonInit() {
guard let view = Bundle.main.loadNibNamed("CustomAlertView", owner: self)?.first as? CustomAlertView else {
return
}
view.frame = bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(view)
}

Collection Cell Content Size

I added a view as parent red view in CollectionViewCell and the next blue subview at the center of the parent view. It works correctly and the sub view goes at the center of the parent view before collection cell size is not changed. But, The cell size is changed by conforming the method from UICollectionViewDelegateFlowLayout protocol and the view is not centered of the cell correctly. How I can solve this issue ?
class ItemCollectionViewCell: UICollectionViewCell {
var parentView: UIView!
var circularView: UIView!
var itemImage: UIImageView!
var itemName: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
// self.updateView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.updateView()
}
func updateView(){
self.clipsToBounds = true
self.parentView = UIView(frame: CGRect(x: 0.0, y: 0.0, width:
self.frame.size.width, height: self.frame.size.height))
self.parentView.backgroundColor = UIColor.red
self.circularView = UIView(frame: CGRect(x: 0, y: 0, width:
self.parentView.frame.size.width / 4 , height:
self.parentView.frame.size.width / 4 ))
self.circularView.backgroundColor = UIColor.blue
self.addSubview(parentView)
self.parentView.addSubview(self.circularView)
self.circularView.center = self.parentView.center
}
}
1]2
Try this code
self.circularView.center = CGPoint(x: self. parentView.bounds.midX, y: self. parentView.bounds.midY)
self.parentView.addSubview(self.circularView)
self.addSubview(parentView)
Try doing:
self.circularView.center = CGPointMake(CGRectGetMidX(self.parentView.bounds), CGRectGetMidY(self.parentView.bounds))
Why this should work? Try checking values of self.parentView.center for each cell, they might not be what you want them to be, because center property gives values with respect to parent view coordinate system.
Remove the following line from updateView()
self.circularView.center = self.parentView.center
Please add it to the following method:
override func layoutSubviews() {
super.layoutSubviews()
self.circularView.center = self.parentView.center
}
e.g.
class ItemCollectionViewCell: UICollectionViewCell {
var parentView: UIView!
var circularView: UIView!
var itemImage: UIImageView!
var itemName: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
// self.updateView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.updateView()
}
func updateView(){
self.clipsToBounds = true
self.parentView = UIView(frame: CGRect(x: 0.0, y: 0.0, width:
self.frame.size.width, height: self.frame.size.height))
self.parentView.backgroundColor = UIColor.red
self.circularView = UIView(frame: CGRect(x: 0, y: 0, width:
self.parentView.frame.size.width / 4 , height:
self.parentView.frame.size.width / 4 ))
self.circularView.backgroundColor = UIColor.blue
self.addSubview(parentView)
self.parentView.addSubview(self.circularView)
}
override func layoutSubviews() {
super.layoutSubviews()
self.circularView.center = self.parentView.center
}
}

iOS - add image and text to titleview with constraints

I am trying to make a custom title view for my nav bar with an image an some text next to it and this is how I did it thank to https://stackoverflow.com/a/47404105 :
class CustomTitleView: UIView
{
var title_label = CustomLabel()
var left_imageView = UIImageView()
override init(frame: CGRect){
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
setup()
}
func setup(){
self.addSubview(title_label)
self.addSubview(left_imageView)
}
func loadWith(title: String, leftImage: UIImage?)
{
title_label.text = title
title_label.font = UIFont.systemFont(ofSize: FontManager.fontSize + 5)
left_imageView.image = leftImage
setupFrames()
}
func setupFrames()
{
let height: CGFloat = 44
let image_size: CGFloat = height * 0.8
left_imageView.frame = CGRect(x: 0,
y: (height - image_size) / 2,
width: (left_imageView.image == nil) ? 0 : image_size,
height: image_size)
let titleWidth: CGFloat = title_label.intrinsicContentSize.width + 10
title_label.frame = CGRect(x: left_imageView.frame.maxX + 5,
y: 0,
width: titleWidth,
height: height)
contentWidth = Int(left_imageView.frame.width)
self.frame = CGRect(x: 0, y: 0, width: CGFloat(contentWidth), height: height)
}
var contentWidth: Int = 0
override func layoutSubviews() {
super.layoutSubviews()
self.frame.size.width = CGFloat(contentWidth)
}
But the problem is that I don't want to hardcode the frame, I want to use constraints but I have no idea where to start.
First take a imageView and set it's constraints according to your requirement and then take a Label besides that imageView set it's constraints too. Do this in your navigationBar where you want to show them.

Add view on UIScrollView without it scrolling (programatically)

I am trying to add a Page Control on top of my UIScrollView Custom class. Everything is wokring fine. But whenever I slide the scrollView, the Page Control will slide along the first Image as well.
Here is my code:
import Foundation
import UIKit
import SwiftyGif
class MBSPagingScrollView: UIScrollView {
var pageControl = UIPageControl()
var imagesArray = [String]()
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
init(frame : CGRect, imagesarray : [String]){
super.init(frame : frame)
imagesArray = imagesarray
self.isPagingEnabled = true
self.showsHorizontalScrollIndicator = false
for (index, imageName) in imagesArray.enumerated() {
var imageView = UIImageView()
if imageName.hasSuffix(".gif") {
let gifManager = SwiftyGifManager(memoryLimit:30)
let gif = UIImage(gifName: imageName)
imageView = UIImageView(gifImage: gif, manager: gifManager)
}
else {
imageView = UIImageView(image: UIImage(named: imageName))
imageView.contentMode = UIViewContentMode.scaleToFill
}
imageView.frame = CGRect(x: CGFloat(index) * self.frame.size.width, y: 0, width: self.frame.size.width, height: self.frame.size.height)
self.addSubview(imageView)
}
self.contentSize = CGSize(width:self.frame.size.width * CGFloat(imagesArray.count),height: self.frame.size.height)
configurePageControl()
pageControl.addTarget(self, action: #selector(self.changePage(sender:)), for: UIControlEvents.valueChanged)
}
func configurePageControl() {
self.pageControl.frame = CGRect(x: 0, y: self.frame.size.height - 50, width: self.frame.size.width, height: 50)
self.pageControl.numberOfPages = imagesArray.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.red
self.pageControl.pageIndicatorTintColor = UIColor.black
self.pageControl.currentPageIndicatorTintColor = MainOrangeColor
self.addSubview(pageControl)
}
// // MARK : TO CHANGE WHILE CLICKING ON PAGE CONTROL
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * self.frame.size.width
self.setContentOffset(CGPoint(x:x, y:0), animated: true)
}
func selfDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(self.contentOffset.x / self.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
And here are images of the problem happening
As you can see the Page Control is sliding with the first image. I need it to stay in the center.
Any help would be appreciated!
I think you can do that by going to Storyboard, and from the left menu of the UI Builder, drag your page control down and put it out of your scroll view and under it, like in the following picture:
Then you'll need to add the suitable constraints to place your page control properly.
Good luck!
If you don’t want it to scroll... don’t add it to the scroll View.
Just add the page control to the self.view instead of to the scroll view.

Can a UIView subclass create other custom view instances?

I have a UIView subclass called LifeCounter, of which I want to create one for each player. I was able to add two views to Main.storyboard, set their class to LifeCounter through the Attributes panel and create multiple instances that way, connect to the view controller, change properties, etc.
What I was thinking about now was creating a larger view, GameHeader, that will hold the LifeCounters and some other supplementary information such as time, game reset button, etc. GameHeader is a UIView subclass, but I can't get it to draw my LifeCounter views in the simulator and I have no idea why.
GameHeader is currently a view dragged into the storyboard, and given it's class with the Attributes panel.
GameHeader.swift
import UIKit
class GameHeader: UIView {
// MARK: Properties
// The Frame
let topFrame = CGRect(x: 0, y: 0, width: 320, height: 200)
// MARK: Initlization
required init() {
super.init(frame: topFrame)
// Adds the player one counter
let playerOneCounter = LifeCounter()
addSubview(playerOneCounter)
}
required init(coder aDecoder: NSCoder) {
// Calls the super class (UIView) initializer
super.init(coder: aDecoder)
}
}
LifeCounter.swift
import UIKit
class LifeCounter: UIView {
// MARK: Propterties
// Starting life total
var lifeTotal = 20 {
didSet {
// Updates the layout whenever the lifeTotal is updated
setNeedsLayout()
}
}
// Creates the UI Labels
// All created views need a defined frame for where they sit
// Is this neccesary outside of the init? Is there a better way?
var counter = UILabel(frame: CGRect(x: 0, y: 20, width: 100, height: 90))
var playerName = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
var winner = ""
// MARK: Initlization
// First init. LifeCounter takes a frame parameter, the adds the labels etc.
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
self.addLifeCounter()
}
required init(coder aDecoder: NSCoder) {
// Calls the super class (UIView) initializer
super.init(coder: aDecoder)
}
func addLifeCounter() {
print("addLifeCounter is running")
// Styles life counter label
counter.textColor = UIColor.blackColor()
counter.textAlignment = .Center
counter.font = UIFont.boldSystemFontOfSize(72)
counter.text = String(lifeTotal)
// Styles playerName label
playerName.text = "Player Name"
playerName.textAlignment = .Center
// Button
let minusButton = UIButton(frame: CGRect(x: 5, y: 110, width: 40, height: 40))
let plusButton = UIButton(frame: CGRect(x: 55, y: 110, width: 40, height: 40))
minusButton.backgroundColor = UIColor.redColor()
plusButton.backgroundColor = UIColor.blueColor()
// Button action
minusButton.addTarget(self, action: "minusLife:", forControlEvents: .TouchDown)
plusButton.addTarget(self, action: "plusLife:", forControlEvents: .TouchDown)
addSubview(playerName)
addSubview(counter)
addSubview(minusButton)
addSubview(plusButton)
}
// MARK: Button actions
func minusLife(minusButton: UIButton) {
lifeTotal -= 1
counter.text = String(lifeTotal)
}
func plusLife(plusButton: UIButton) {
lifeTotal += 1
counter.text = String(lifeTotal)
}
}
init(coder:) is the initializer that is called when a view is created from a storyboard or nib. If you move the code that creates and adds LifeCounter instances there, it should work.
A good strategy to make it more reusable is to create a setup method that is called from both initializers so that it will run whether it comes from a storyboard/nib or it is instantiated programmatically.
class GameHeader: UIView {
let topFrame = CGRect(x: 0, y: 0, width: 320, height: 200)
required init() {
super.init(frame: topFrame)
self.setupSubviews()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupSubviews()
}
func setupSubviews() {
let playerOneCounter = LifeCounter()
addSubview(playerOneCounter)
}
}
I discovered the error was in putting the LifeController drawing code inside the init(frame:) block instead of with the Coder block. When a view is added through Interface Builder, it uses init(coder:) instead of init(frame:)

Resources