Blog

  • 맥북 M1 14인치 가성비 케이스 추천 JETech 슬리브

    맥북 M1 14인치 가성비 케이스 추천 JETech 슬리브

    싱가포르 아마존에서 저렴하게 구매한 JETech 슬리브

    이전에 사용하던 맥북 케이스는 튤레 제품 15인치 전용이었다. 2015년에 구매해서 8년을 사용했다.

    JETech 맥북 14인치 슬리브

    패브릭 재질의 슬리브이다. 중국산인거 같은데 가격이 저렴해서 구매했다. 생각보다 퀄리티가 괜찮았고 마감처리도 마음에 들었다.

    무엇보다 저렴한 가격이다. 23.79불에 구매
  • 아시아나 항공 골드에서 다이아몬드 승급기준

    아시아나 항공 골드에서 다이아몬드 승급기준

    코로나로 인하여 아시아나 항공 승급기준이 2년 -> 4년으로 연장되었다.

    즉, 4년이내에 탑승 마일리지와 제휴 마일리지를 쌓으면 승급이 된다.

    아시아나 항공 카운터에서 승급기준 확인

    공항에 가면 체크인 하는 곳 왼쪽에 회원등급 관련 카운터가 있다. 여기 직원한테 문의하면 아래 사진과 같이 친절히 적어준다.

    만년 골드등급에서 다이아몬드로 넘어가기 위한 조건은 다음과 같다.

    총 4만 마일리지를 적립해야 되는데 아래와 같은 조건으로 적립해야 된다.

    탑승마일리지 3만 마일리지

    제휴 마일리지 1만 마일리지

    탑승 마일리지 나라별 적립 기준

    편도기준 적립표는 위의 표를 참고 바란다.

    싱가포르 왕복 비행기를 타면 5768 마일리지가 적립된다.

    3만 마일리지를 탑승 마일리지로 채우려면 6번 왕복하면 된다.

    제휴 마일리지

    이건 그냥 신용카드중에 카드결제시 마일리지 적립해주는 카드를 사용하면 채울 수 있다. 보통 카드사별로 조건이 다르지만 메인 신용카드를 마일리지카드로 쓴다면 1만 마일리지를 4년이라는 시간에 채우는 것은 크게 어렵지 않다.

    다이아몬드 등급 혜택

    비즈니스클래스 입장시 탑승 가능.

    수화물 골드보다 빨리 나옴.

    수화물 2개까지 무료. (단 추가 수화물은 20kg까지)

    우수회원 탑승 보너스 마일리지 10% 추가 적립

    위의 4가지가 가장 큰 혜택이다

  • How to implement MasonryLayout (Pinterest Style) using UICollectionViewCompositionalLayout

    How to implement MasonryLayout (Pinterest Style) using UICollectionViewCompositionalLayout

    In this post, I’ll share sample code for implementing MasonryLayout using UICollectionViewCompositionalLayout.

    UICollectionViewCell

    I defined a very simple UICollectionViewCell. It consists of basic UI components such as Photo, Title and Description.

    NSCollectionLayoutGroupCustomItem ( >= iOS 13)

    Apple Documents

    This is important to implement masonryLayout. Unfortunately It doesn’t support NSCollectionLayoutSize. (It supports GCRect only)

    It means we need to calculate the cell’s contents size.

    So We need to write 2 main logics.

    Step 1. Calculate Cell’s size

    To calculate size, We need to know cell’s width first.

    We knows collectionView’s width by accessing UICollectionViewCompositionalLayout’s layoutEnvironment.container.contentSize.

    private func masonryLayout(contentInset: NSDirectionalEdgeInsets) -> UICollectionViewCompositionalLayout {
        let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, layoutEnvironment in
        var items = [NSCollectionLayoutGroupCustomItem]()
        let snapshotItems = self?.dataSource.snapshot(for: .item).items
        let numberOfItems = snapshotItems?.count ?? 0
        let contentSize = layoutEnvironment.container.contentSize
                ...
    }
    

    Cell’s width

    We have 2 columns. so Cell’s width is contentSize.width / columns.

    If we set interItemSpacing and NSDirectionalEdgeInsets then logic is below

    let horizontalSpacing = contentInsets.leading + contentInsets.trailing
            let spacing = (columns - 1) * interItemSpacing + horizontalSpacing
            return (contentSize.width - spacing) / columns
    

    Cell’s Height

    Calculate UIImageView’s size

    In this example, I set image ratio as 4:3. So Image Height is cell’s width * 0.75.

    Calculate UILabel’s size

    To get the correct size, We need to provide the details such as Font, LineBreakMode and NumberOfLines.

    Also to get the correct bounding size we set NSString.DrawingOptions.

    If you want to know more details about drawing options visit Apple documents.

    extension UILabel {
        var textHeight: CGFloat? {
        guard let labelText = text else {
            return nil
        }
        let attributes: [NSAttributedString.Key: UIFont] = [
                .font: font
    ]
        let labelTextSize = (labelText as NSString).boundingRect(
                with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
                options: .usesLineFragmentOrigin,
                attributes: attributes,
                context: nil
            ).size
            return ceil(labelTextSize.height)
        }
    }
    
    func height(text:String, font:UIFont, numberOfLines: CGFloat, lineBreakMode: NSLineBreakMode, width:CGFloat) -> CGFloat{
        let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: UILabel.noIntrinsicMetric))
        label.numberOfLines = numberOfLines
        label.lineBreakMode = lineBreakMode
        label.font = font
        label.text = text
        return label.textHeight ?? 0
    }
    

    Step 2. Calculate Cell’s Position in a Group

    We need to layout cell’s position in a Group. CGRect has origin x, y and width, height. At step 1, We already calculated the cell’s width and height. In step 2, We need to calculate origins.

    Calculate cell’s origin X and Y

    Let’s take a look how to layout cells. (row is indicating item’s index)

    First Item (row: 0, column: 0)

    It’s origin is 0, 0

    Second Item (row: 1, column: 1)

    It’s origin y is 0. But x is first item’s width + interItemSpacing.

    let originX = (cellWidth + interItemSpacing) * columnIndex
    

    Third Item (row: 2, column: 0)

    Its origin x is zero because columnIndex is zero. But origin y is calculated by maxY between First Item and Second Item. Second Item’s maxY is smaller than First Item. So We picked this to layout Third Item.

    UICollectionViewCompositionalLayout for MasonryLayout (Pinterest Style)

    Here is full source code.

    cachedSize used for saving cell’s height. It will be called when you call a reloadData or applySnapshotUsingReloadData. (In this sample, I used dictionary but you can considering NSCache)

    private func masonryLayout(contentInset: NSDirectionalEdgeInsets) -> UICollectionViewCompositionalLayout {
        let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, layoutEnvironment in
        var items = [NSCollectionLayoutGroupCustomItem]()
        let snapshotItems = self?.dataSource.snapshot(for: .item).items
        let numberOfItems = snapshotItems?.count ?? 0
        let contentSize = layoutEnvironment.container.contentSize
        let itemProvider = MasonryLayoutProvider(
                    columns: 2,
                    interItemSpacing: 16,
                    collectionWidth: contentSize.width,
                    contentInsets: contentInset) { [weak self] row, cellWidth in
        //Use Cached Height
        if let cachedHeight = self?.cachedSize[row]?.height         {
            return cachedHeight
        }
        let height = snapshotItems?[row].estimatedHeight(width: cellWidth) ?? 0
        self?.cachedSize[row] = .init(width: cellWidth, height: height)
            return height
        }
        for i in 0..<numberOfItems {
            let item = itemProvider.makeLayoutItem(for: i)
            items.append(item)
        }
        let groupLayoutSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(itemProvider.maxColumnHeight())
        )
                
        let group = NSCollectionLayoutGroup.custom(layoutSize: groupLayoutSize) { env in
            items
        }
        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = contentInset
        section.boundarySupplementaryItems = [
                    .init(layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(10)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        ]
            return section
        }
        return layout
    }
    

    For more details, You can checkout my repository

    References

    https://www.kodeco.com/4829472-uicollectionview-custom-layout-tutorial-pinterest

    https://github.com/eeshishko/WaterfallTrueCompositionalLayout

  • 싱가포르 콘도에 방충망 셀프 설치하기

    싱가포르 콘도에 방충망 셀프 설치하기

    싱가포르는 생각보다 모기는 없는 편이다. 그래서인지 방충망 없이 사는 집들이 대부분이다. 하지만 여긴 개미랑 도마뱀이 존재한다.

    아무리 높은 층을 살아도 신기할 정도로 도마뱀이 보인다. 하루종일 걱정없이 창문을 열고 싶다면 방충망 설치를 추천한다.

    생각보다 어렵지 않다. 이케아 가구 조립보다 살짝 시간이 걸린다는 정도?

    방충망 구매

    일단 나는 라자다에서 방충망을 구매했다. 대충 마그넷 메시 어쩌구로 검색하니 여러제품들이 나왔다.

    우리집은 2 베드룸인데 여기에 창문이 총 4개다. 대충 사이즈를 넉넉하게 측정 후 4개를 구매했다. 총 비용은 120불 정도 들었다.

    설치과정

    구성품을 보면 본드, 줄자, 방충망 (폴리에스테르 같은 재질임), 자석 그리고 니퍼랑 커터칼 까지 포함되어있다.

    설명서에도 잘 나와있지만 다음과 같이 진행하면 된다.

    1. 일단 자석을 창틀에 붙인다.

    2. 창틀에 붙인 자석에 맞닿게 다른 극의 자석을 붙인다.

    3. 자석에 붙어있는 양면 테이프 떼어낸다.

    4. 자석의 길이에 맞게 방충망을 댄다.

    5. 방충망을 댄 후 자석부분에 본드를 바른다.

    6. 본드 바른 부분에 긴 플라스틱을 창틀 길이에 맞게 잘라내면서 붙인다. (이 부분이 그나마 좀 난이도 높음)

    7. 모서리 부분은 ㄱ자로 생긴 플라스틱 연결부위로 잘 연결하자.

    8. 삐죽 튀어나언 방충망을 칼로 정리한다 (끝!)

    방충망이 설치된 모습

    마치며

    좀 고생을 했지만 만족도가 높다. 금액도 저렴한 편이고 하루종일 창문 열고 생활해도 벌레하나 없다. 도마뱀도 우리집에서는 1년반 넘도록 한번도 못봤다.

    마음 같아서는 발코니부분에도 방충망을 설치하고 싶다

  • 아이폰 여행 필수앱 트레블크럼 여행 예산 환율 및 경비 기록 무료앱

    아이폰 여행 필수앱 트레블크럼 여행 예산 환율 및 경비 기록 무료앱

    여행갈 때 내가 어디서 얼마를 썼는지 기록하는 유용한 앱이다.

    이 앱의 가장 큰 장점

    – 무료앱

    – 현재 위치기반 날씨 정보 제공

    – 하루에 얼마나 걸었는지 걸음수 제공

    – 현재 위치기반 환율 정보 제공 (금액 입력하면 알아서 원화로 표시)

    – 경비 예산 설정

    – 예산 대비 얼마나 지출했는지 그래프로 제공

    트레블크럼 기능들

    먹고 쓰고 기록하는 앱

    다운로드 링크

    트레블크럼

    마치며

    광고아님. 직접 개발함. 필요한 기능이나 불편사항 있으면 코멘트 남겨주세요

  • 싱가포르에서 플라타가 제일 맛있는 곳 Thasevi Food 1960년부터 플라타만 만든 곳

    싱가포르에서 플라타가 제일 맛있는 곳 Thasevi Food 1960년부터 플라타만 만든 곳

    오늘은 Jalan Kayu 지역에서 가장 유명한 플라타 맛집을 다녀왔다. 싱가포르 현지인들이라면 Jalan Kayu가 플라타로 유명하다는 것을 안다.

    24시간 영업

    오리지널 플라타라고 쓰여있다. 바로 왼쪽에는 새우국수로 유명한 블랑코 새우국수집도 있다.

    메뉴

    굉장히 다양한 플라타 메뉴들이 있다. 일단 이 집에서 유명한 메뉴는 에그 플라타, 치즈 플라카, 바나나 플라타 등이 있다.

    플라타를 주문하면 기본 소스로 살짝 매콤한 커리가 나오는데 찍어먹으면 플라타의 맛과 잘 어울린다.

    이곳은 티로도 유명한데 Teh라는 티를 판다.

    Teh는 일반적인 블랙티이고 Teh-O는 우유가 들어간 블랙티이다.

    마치며

    여기 구글 후기를 보면 약간 안좋은 평도 있다. 주로 너무 기름지게 나왔다 등의 평인데 최근에는 이런 후기를 의식했는지 플라타를 아주 겉은 바삭하게 속은 촉촉하게 바로 갓구어서 잘 서빙해준다.

    내 생각엔 사람들이 많이 방문하는 주말 아침 즈런치 타임에 가면 최상의 플라타를 먹을 수 있을 것 같다. 주문이 많이 들어오기에 바로바로 구워서 준다.

    재방문 의사 100프로인 식당이다

  • 아이폰 15 프로 네츄럴티타늄 1테라 바이트 개봉 (언박싱)

    아이폰 15 프로 네츄럴티타늄 1테라 바이트 개봉 (언박싱)

    오늘 오차드가서 아이폰 15 프로를 샀다.

    그동안 아이폰 13 미니 256기가를 써왔는데 완충을 해도 반나절도 잘 못 버티는 배터리에 질렸다.

    왠만하면 폰을 4년정도는 쓰는데 아이폰 미니 13은 이재 그만 쓰기로 결정.

    싱가포르 오차드 애플 스토어

    싱가포르에서는 무이자 24개월 할부로 구매가 가능하다. 온라인으로 채팅 연결을 통해야지만 24개월 할부 결제가 가능하고 아니면 애플 스토어 직접 방문하는 방법이 있다.

    나는 색상도 직접 확인 할 겸해서 방문했다.

    화창한 오차드거리
    암튼 구매해왔다
    얼마 안되지만 회사 할인도 받아서 기분 업

    언박싱

    투명 케이스랑 아이폰 15 프로 1테라 구매했다

    정말 단촐한 구성품 케이슬 달랑 한개

    아이폰 15 프로 언박싱 후 느낀점. 오 정말 가볍다.

    무게비교

    느낌상 아이폰 미니랑 별 차이가 없게 느껴져거 설마 하는 마음에 무게를 재봤다. 둘다 액정보호필름 뗀 상태로 측정함.

    아이폰 15 프로 – 187 그램
    아이폰 13 미니 – 140 그램

    역시 아이폰 15 프로가 당연히 더 무겁긴 했다.

    백업과정

    영혼까지 백업하려면 폰에서 폰으로 하라는 이야기가 있길래 그렇게 진행했다. (영혼까지 백업 안됨)

    결론

    너무 만족스럽다. 특히 배터리

  • 스쿠트항공(Scoot) KrisFlyer 마일리지로 항공권 결제하는 방법

    스쿠트항공(Scoot) KrisFlyer 마일리지로 항공권 결제하는 방법

    싱가포르 항공의 저가 브랜드인 스쿠트 항공은 KrisFlyer 마일리지로도 항공권을 구매할 수 있다. 싱가포르 항공을 타든 스쿠트를 타든 KrisFlyer 마일리지로 적립된다.

    스쿠트항공 웹사이트로 예약하기

    스쿠트항공 앱도 있지만 앱은 결제 방법에 KrisFlyer 마일리지 결제 옵션이 아에 없다. 반드시 웹 사이트인 https://www.flyscoot.com/en 들어가서 결제하자.

    현재 19740 마일리지는 싱가포르 달러로 환산하면 419.76달러 (약 42만원 정도) 된다.

    항공권 구매 가격에서 마일리지를 사용하면 저 금액만큼 제외되고 나머지 금액만 신용카드로 결제가 가능하다.

    마치며

    스쿠트 항공은 웹사이트랑 앱이랑 너무 다르다. 예를 들어 웹 체크인이 웹사이트에서는 되는데 앱에서는 안된다던가 하는 일이 빈번하게 발생한다. 스쿠트 항공을 이용한다면 웹 체크인은 필수다. 그리고 마일리지가 있다면 반드시 웹 사이트에 들어가야 사용할 수 있다. 언젠가는 앱도 지원해주겠지….

  • 베트남 호치민 미슐랭 맛집 벱메인 (Bep Me In)

    베트남 호치민 미슐랭 맛집 벱메인 (Bep Me In)

    미슐랭 맛집인 벱메인을 다녀왔다.

    https://maps.app.goo.gl/jqaYnBboWHnGEf4D8

    호치민에 와서 며칠 연속 쌀국수만 먹다가 좀 더 다른 메뉴를 먹고 싶어서 들린 곳. 결론적으로는 2박 3일 방문했던 모든 음식점중에서 제일 맛있었다.

    우리가 시킨 메뉴들.

    호치민에 들린 다면 꼭 방문해야 될 강추 맛집이다.

    모든 메뉴가 다 맛있었지만 특히 맛있었던 메뉴는 오므라이스처럼 생긴 3번째 메뉴와 피자처럼 생긴 첫 번째 메뉴 그리고 샌드위치 처럼 생긴 메뉴 모두 정말 맛있었다.

  • 싱가포르 카페 Anna’s

    싱가포르 카페 Anna’s

    여기는 Punggol 지역 Oasis LRT역 근처에 있는 카페다.

    시간이 조금 남아서 잠깐 들린 곳.

    분위기도 좋고 커피도 맛있었다. 이 근처에 살거나 올일이 있다면 추천할 만한 곳이다. 재방문 의사 100%